You are on page 1of 422

l·•·.

-
11� - --

O 用框囹描述每令子系统, 匡分BSP和公用部分
- -
、.、

Btoadview"
www.broadvlew.coffl.cn

O比絞几令有代表性的硬件平台的实塊
O比絞Android的不同版本
O展示Android系统没计的核心思路
O列出相共代碼路椏
©根据实际卉友経验編写


nuro1
板级支持甸硬件相天子系统
韩超等著

吝坌#豔岑!:
Btoadview。 搏文檁氝 ·IT 出麻鼴晨晶蘑
www.broad,rlew.com上'
技木凝聚实力·夸血刨新出版

本节內容
本节提供了系统化的Andra這系统的玕友知讠只, 以硬件相芙的子系统

为核心, 主要包括几令方面的內容:

·硬件相美的子系统的特束。

·几令不同的硬件平台的Linux內核結杓。

.每令子系统的急体結杓和BSP結杓。

.每令子系统的BSP的实视要羔。

.具体硬件在Linux內核与Android硬件抽象扆相栄的实瑰。

___
__

,�,q�-


"
.
「II
8|

3
4_
8
o,
s
lN
_

@昱翌戇
`8
2

2
o'
7

9
1
8

策划編輯:李 冰

§el 亮任編輯:高洪霞
封面没计:侯士卿
定价: 59.00 元
板级支持卣硬件相天子系统
韩超等著

念孑11:° 出縴� 社
Publishing House of Electronics Industry
北京•BEIJING
內容简介

本节以硬件相美的子系统为核心,提供具有完整知沢体系 Android 系统级的升友知沢。本节造定了


几介流行的硬件作为參考乎台, 涙者可以很容易地得到硬件和升源代碼。本节突出了硬件相美的子系统
的特燕, 展示了几令不同的硬件平台的內核結杓, 介紹了每今子系统的惡体結杓和 BSP 結杓 、 每令了
系统的 BSP 的实瑰要燕, 以及具体硬件在 Linux 內核与 Android 硬件抽象扆相美的实珉。
本节适用于各癸 Android 技木群体, 也适用于嵌入式 Linux 的技木人贝了解实际系统。作者根据丰
富的升及經验和対 Android 系统友展 5 年的惡結完成本节, 希望为 Android 系统的升友者和挙刁者提供
切实有效的牾助。

未經讠午可, 不得以任何方式夏制或抄袈本节之部分或全部內容。
版枚所有, 侵枚必究。


囹节 在版編目(CIP)致据

Android 板级支持与硬件相美子系统/韩超等著一北京: 屯子工並出版社, 2013.10


ISBN 978-7-121-21348-9

I. (DA … II. CD韩… Ill. CD移劫終端-硬件-基本知讠只 LV. (DTN929.53

中因版本招节綰 CJP 敖据核字 (2013) 第 202974 另

策划編緝: 李 冰
蠻任編緝高洪霞
印 刷: 三河市戏峰印刷裝讠了有限公司
裝 汀: 三河市戏峰印刷裝汀有限公司
出版友行: 屯子工�l出版社
北京市海綻巨万蒂路 173 信箱 郎編 100036
升 本: 787X 1092 1/16 印張: 26.25 字敖: 672 千字
印 次: 2013 年 10 月第 l 次印刷
印 敷: 3000 朋 定价: 59.00 元

凡所朗实屯子工�l出版社囹节有缺捩祠題, 清向朗实节店调換。 若节店售缺, 清与本社友行部联系,


联系及鄘酶屯话: (010) 88254888。
殷品投泝清友郇件至 zlts@phei.com.cn, 盜版侵枚举扳清友鄘件至 dbqq@phe1.com.cn。
服各熱紱: (010) 88258888。
前 言

升友者的需要
Android系统已經推出了将近5介年失了, 从1.0版本 一直到本节写作时的4.2版本。
作为其裁体的硬件也經迥了多次升级。到今天,Android没各已經成为硬件的集大成者。硬
件方面的升友 一直是升友的雉燕, 凡是 一 介完整 Android 没各的升友者, 元诒灶于严並鏈
的哪 一 令阶段, 都不可避免地要灶理与硬件相美的冏題。
Android的升笈者通常面対几令方面的雉燕:
e Android系统的代碼厐大, 雉以把握硬件相美的调试思路。
.不清楚軟件和硬件之阅的直接哭系。
.対某令硬件平台的知讠只和經验不适用于其他硬件平台。
e Android系统的版本升级迥程中, 与硬件相美的部分常常友生重大変劫。
另外 一 介客视的情況是, 目前 一 般姓理器或者基本硬件平台的 BSP (Board Support
P ackage, 板级支持包)部分都是由芯片的「商统 一完成的, 并且已經超近于成熟。 因此,
升友者的主要工作不再是杓建完整的BSP, 而是调试和修改現有的BSP。


本节 特色
本节的目的是要为玕友者提供切实有效的牾助。 針対升友者的瑰实情況, 本节主要具
有以下几令特 ,亞:
0用框囹描述每一 介硬件相哭子系统的結杓, 并巨分BSP 部分和公用部分。
龕造用多介流行的硬件平台, 対比其中不同的实瑰和相同的理念。
.対比Android 2.3和Android4.x的实現, 展示硬件相哭部分的升级。
.対厐大的系统去耦合, 展示Android 一 些原始的核心没计思路。
• 列出每 一 令部分相美的代碼路往。
0简要列出代碼的美鍵部分。
.根据实际經验編写, 工程性強。
通迥対每一 介子系统的挙刁, 渡者可以了解瑰有 Android 系统的結枸, 經迥深入理解
后, 可以明白Android 系统的没计思路。 挙司后, 如果要在系统中增加 一 介新的非杯准硬
件, 渙者也能比絞容易地完成。
本节特別迭定了Nexus One、 Nexus S、GalaxyNexus等几款手机作为參考平台。 其中

介很大的优燕就是以上几介平台都是Google iA定的, 具有典型性, 并且它亻i'J从內核到
Android系统的代碼都是升源的,渡者可以很容易荻得。呈然以上几介硬件平台不是最新的,
但是根据 嵌入式 soc 的特熹, 高通的姓理器都与NexusOne的QSD 8x灶理器奕似, 三星
的姓理器都与 Nexus S的Exynos灶理器奕似, 德州仅器的灶理器都与Galaxy Nexus的
OMA P灶理器癸似。 因此, 迏祥的造捅既方便又具有「泛的适皮性, 岂涙者熟悉了迏几介
硬件平台后, 置于M a rvel、 Freescale和NV汕a等公司的平台可以实現融佘貫通。
Android系统可以被视为一介功能完各的机器人。 其中与硬件相美的 BSP部分, 則是
迏介机器人的根基和經豚, 呈然占的比重不大, 却是系统的哭鍵所在。
本节的理念可以用下面的囹來表示。

本节內容
本节提供了系统化的Android系统的升友知讠只, 以 硬件相芙的子系统为核心, 主要包
括以下几介方面的內容:
硬件相芙的子系统的特燕。
.几今不同的硬件平台的Linux內核結杓。
• 每今子系统的惡体結杓和BSP結杓。
.每令子系统的BSP的实現要煮。
0具体 硬件在Linux內核与Android硬件抽象展相美的实瑰。
人的知沢和經验本身是阿狀結杓, 各部分相互哭联, 錯綜夏奈。但是作为出版的节籍,
則必須将其串行成章节的形式, 本节惡体上是以橫向結杓釆迸行組鋇的, 大部分章节是針
対每 一今 硬件相失子系统的描述, 每 一章的組鋇結杓也比絞奕似。

本节漯者
本节适用于各奕Android技木群体, 也适用于嵌入式Linux的技木人昃了解实际系统。
作者対漾者有以下几介方面的建汶:
國根据节中提供的知讠只和經验, 対照Android的源代碼, 有相皮的Android没各, 迏
三者的結合是最理想的挙刁坏境。
龕要首先理解宏戏結杓, 再研究细枝末节, 硬件相美子系统的很多代碼并非在任何情
況下都令适用, 涙者需要了解其适用的汤景。

·IV·
e BSP部分的升岌偏重下展,渙者不要迥于依賴界面,而要司愤査看系统日志,从Linux
系统杯准的没各和特殊文件系统中荻取信息, 并使用各秤命令行工具调试。
a硬件抽象扆的目的是为了适配各秤硬件, 很多程序的結杓看似冗余, 却正是BSP没
计的精华所在, 迏也是渡者需要美注的內容。
0夯实Linux的基絀対Android升友也非常重要, 対Android的BSP升友尤为重要。
龕在升友的迥程中, 可能用到很多不同的硬件平台, 要根据本节的思路掌握查看硬件
信息和硬件相哭代碼的方法。

本节作者
本节的規划和统筹由中囯大陡的韩超完成, 韩超在Linux和Android頲域具有丰富的
一綫升友經验。 本节內容米源于工作在不同練域的玕友者多年的經验。 韩超完成了本节內
容的主要部分, 焱多不同規模的企並玕友成果也为本节的編写提供了重要的素材。 參与本
节編写的坯有崔海斌、 于仕林、 張宇、 張超、 趙家维、 黃亮、 沈棪、 徐威特、 栃钅玉、 易若
劫、 曹道別、 梁泉等。

. V.
H

2.5.1 Android2.3 中的实現方式 … ··22


第1 章 Android 的 BSP 和子系统升友 1
2.5.2 Android 2.2及之前的
1.1 Android板级支持工作概述… …… ·I 实現方式·································23
1.1.1 Android的升放源 2.5.3 Android4.x中的实瑰方式 … ··24
代碼工程和BSP...…... ……........ 1
2.6 与硬件抽象扆相美的
1.1.2 Android的系统結杓….….… … ·I 框架扆目杲 ······································24
1.2 Android的玕岌坏境和源代碼.… 2 2.6.1 一直保持不変的代碼 ········24
1.2.1 Android的升友坏境….. … … …"2 2.6.2 框架展的本地代碼…… … .……··24
1.2.2 源代碼合庠............................... 3 2.6.3 音颜视颜相美的代碼........…25
1.3 BSP 模玦和相哭子系统 5
第3 章 Android的Linux內核和驱劫 26
1.3. l Android的 asp ......................... 5

1.3.2 BSP和硬件相夫子系统 ........... 6 3.1 Android的Linux內核概述… …···26

1.3.3 不同突型的Android没各 .. ... 7 3.1.1 几介內核工程...…····················26


3.1.2 內核工程的編诵工具镱… … ···26
第2 章 Android 系统 BSP 部分工作 8
3.1.3用户空冏哭注的內容…··········27
2.1 Android的 BSP 部分 3.2 Android 考用驱劫和組件 ·············27
工作概述............................................ g
3.2.1 屯源管理部分......…… … ……… ··27
2.2 BSP 的全局部分......…...................... g 3.2.2 staging中的組件和
2.2.1 源代碼工程板级別 驱幼程序 ·································28
支持部分 ................................... 9
3.2.3 几介主要核心模坎…….……… ··32
2.2.2 硬件相失的代碼改劫. … ...... II 3.2.4 鋪助的模玦和改劫……………… 35
2.3 Android的Linux操作系统......... 14 3.3 goldfish平台的內核和驱劫 37
2.3. l Android中的Linux操作 3.3.1 goldfish平台和內核概述······37
系统的特定內容 … …………… …14 3.3.2 goldfish体系結杓移植….....…38
2.3.2 Android的Linux的 3.3.3 goldfish的相栄没各驱劫 ···-40
基本支持................................. 15 3.4 高通 MSM平台的內核和
2.3.3 Android各介硬件没各的 驱劫 ····················································42
驱劫程序 ................................. 16 3.4.1 平台概述·································42

2.4 Android 的硬件抽象扆................ 17 3.4.2 体系結杓移植......................…43

2.4.1 硬件抽象扆的地位和功能…... 17 没各驱劫程序.............…··········4 3


3.4.3
44
2.4.2 硬件抽象扆接口方式 ............. 18 3.5 三星平台的內核和驱劫 ··········
3.5.1 平台概述·································44
2.5 各介子系统的移植方式 22

•VI•
3.5.2 体系結杓移植 …..…. .....….….… 45 榆入配置文件…..….................g7
5.4.2
3.5.3 驱劫程序部分...… .....…···········45 5.5 用户榆入BSP的实瑰 ............ g9
3.6 德州仅器OMAP平台的 5.5.1 模姒器 中的实現 ·……..…........g9
內核和驱劫······································46 5.5.2 NexusOne系统中的实現 ……·90
3.6.1 平台概述 ·································46 5.5.3 NexusS系统中的实珗 …….. …93
3.6.2 体系 結杓移植............….......... 47 5.5.4 Galaxy Nexus系统中的
3.6.3 驱劫程序部分 ························· 47 实瑰 .........................................94

第4章 晁示系统 ….....… ..........….............. …… 49 第6章 伟感器系统...... …...............…........…·96

4.1 晁示系统概述…… …………..……..…… 49 6.1 伟感器系统概述 ·……… ….....… ..… ··96


4.2 昱示子系统結杓 ·…....……… …… …… 50 6.2 伟感器子系统的結杓 ·…….…………97
4.2.1 恙体結杓·································50 6.2.1 惡体結杓.................................97

4.2.2 核心結杓和U1庠…… ….………51 6.2.2 本地框架扆 .............................9g


4.2.3 Surface本地部分… …… ……… ··54 6.2.3 伟感器系统的JN1….. ....…··99
4.2.4 Java扆的Surface的灶理 56 6.2.4 伟感器系统的Java扆 100

4.3 晁示BSP的結杓····························57 6.3 伟感器BSP的結杓 ….. .. …....... 101


4.3.1 Framebuffer驱劫程序 … ……… ·57 6.3.1 驱劫程序 ...............................10 1
4.3.2 gr alloc硬件抽象扆…… ·······… 5 9 6.3.2 硬件抽象扆的內容… …… ..… ··102
4.4 晁示BSP 的实瑰 ………….. ….……… 61 6.4 伟感器BSP的实現 …………..……104

4.4.1 模姒器晁示系统的实瑰··········61 6.4. 1 仿真器的实現… ……·….. ……10


4
4.4.2 NexusOne系统的实現……… ··68 6.4.2 NexusOne系统实現 ….. ……106
4.4.3 NexusS系统的实現 …….……··72 6.4.3 NexusS系统实現 ………… ….. ,01
4.4.4 Galaxy Nexus系统的实瑰···73 6.4.4 Galaxy Nexus系统实現 ·109

第5章 用户榆入系统················…...... …·········75 第7章 音頻系统............................................ 111

5.1 用户榆入系统概述………………… …·75 7.1 音颜系统概述……..........................111


5.2 Android 2.3用户諭入子系统 ……7
6 7.2 音颜子系统結杓 ………… ...…..…… ·112
5.2.1 惡体結杓·································76 7.2.1 惡体結杓............................... 112

5.2.2 本地框架的几介部分… ••… 77 7.2.2 Audio的本地框架扆.. ……… ·113


5.2.3 JNI........................................... 80 7.2.3 Audio系统的JN1和
5.2.4 Java昆 的部分.. ……... .. …........ gJ Java昆................................... 114
5.3 Android 4.2的用户榆入 7.3 音颜BSP的結杓 …… …………… …··116
子系统結杓......................................81 7.3.1 Audio驱劫程序 …………....... 116
5.3.1 惡体結杓·································81 硬件抽象扆的內容 …… ………·120
7.3.2
5.3.2 lnputManagerService的 7.4 音颜BSP的实瑰……… ……… ……··124
实瑰 ·········································82 7.4.1 通用的 Audio系统实瑰.. .....124
······84
5.4 用户榆入BSP的結杓 7.4.2 基于 oss 的实現方式.........129
5.4.1 Input驱劫程序 ….……...…… …84 7.4.3 基于 ALSA的实現方式 .. .. 130

·VII•
7.4 .4 MSM平台和NexusOne 9.4.3 Nexus S系统的Camera
系统的实珗·…. …….....….... …·132 实珗·······································180
7.4.5 Nexus S系统的实現……....... ,37
第10章 l
OpenGL 3D弓 擎·………..……..…·184

第8章 视頻量加榆出系统……………………· 140


IO.I OpenGL系统概述…………………·184
8.1 视颜疊加榆出系统概述 140 I0.2 OpenGL系统的結杓 184
8.2 视颜榆出子系统的結杓 141 I 0.2.1 OpenGL和OpenGLES的
8.2.1 Overlay系统的結杓………···· 141 杯准結杓·····························184

8.2.2 本地框架展 …… … . ················· 142 惡体結杓·····························186


10.2.2
8.3 视颜疊加榆出BSP結杓 ···144 I 0.2.3 OpenGL庠的调用者………··188
8.3.1 移植的內容 .....……·······1 44
OpenGLBSP的結杓........ …···190
………
10.3
8.3.2 驱劫程序·······························144
10.3.1 移植的內容··························190
8.3.3 硬件抽象展的內容…...…. …··1 44
10.3.2 OpenGL移植扆的接口 190
8.3.4 视颜諭出的调用者 …... … ……146 10.3.3 OpenGL的调用和測试·······192
8.3.5 使用Overlay的
10.4 OpenGLBSP的实瑰·················193
敖据流情況... ….... …..……····· 148
10.4.1 Android軟件OpenGL的
8.4 视颜諭出BSP的实瑰················· 149
实瑰·····································193
8.4.1 骨架实現·······························149
10.4.2 NexusOne系统的实現……195
8.4.2 OMAP 系统的实瑰… ………... 151
I 0.4.3 Nexus S系统的实現 ………··195
8.4.3 Nexus S系统的实 現…… ……· 156
10.4.4 GalaxyNexus系统的
9 照相机系统…... …························…...1 59
第 章 实現 ·····································196

9.1 . 照相机系统概述… ……. …··············159 第11章 l


OpenMax弓 擎… …...…..….... …······197
9. 2 照相机子系统的結杓…··············1 60
11.1 OpenMax系统概述 ……… …… …·1 97
9.2.1 照相机系统的結杓 …… ………160
11.2 OpenMax子系统結杓···············197
9.2.2 Camera的本地展…… …………1 61
11.2.1 OpenMax系统的結杓······197
9.2.3 Camera的JNI和Java扆 …..1 65
11.2.2 Android中OpenMax的
9.3 照相机BSP的結杓 ·…… ………….. 166
适配展 ·································201
9.3.1 移植的內容.…......… …···········166
lI .3 OpenMaxBSP的結杓 203
9.3.2 Videofor4 Linux
驱劫程序······························· 166 11.3.1 OpenMaxIL展的接口········203

9.3.3 硬件抽象昆的內容 . …...... …·168 11.3.2 Android的OpenMax···········207

9.3.4 照相机系统上下展的 II .4 OpenMaxBSP的实現 207


哭系·······································173 11.4.1 OpenMaxIL实現的內容····207

9.4 照相机BSP的实瑰 …...… ………···175 11.4.2 OMAP3 的OpenMax IL

9.4.1 粧实瑰···································175 实現的結杓和机制…... ……·208

9.4.2 NexusOne系统的Camera 11.4.3 OM战4 的OpenMaxIL


实 現·······································178 实現·····································21 3

·VIII·
第 12章 位玦夏制········································· 2 6
1 14.3 BSP的結杓··································243

12.1 位玦夏制概述… .…… ……...…····· 216 14.3. l 仂讠又和驱劫程序…………······244

12.2 位玦夏制子系统結枸 216 14.3.2 本地代碼的配置部分 ········245

12.2.1
惡体結杓····························· 2 6 14.4 Android 4.2的 盔牙系统·········246
1
12. 2. 2 copybit的调用者 ·············· 2 7 14.4.1 系统 結杓 ············….…·····246
1
12.3 位玦夏制BSP的結杓 ········· 218 14.4. 2 藍牙 硬件模玦.. …..............…246

12.3.1 驱劫程序····························· 218 14.4.3 藍牙系统的本地展部分 ··252

1 2.3.2 硬件抽象尼的接口 ··········· 218 14.4.4 BlueTooth包························252

1 2.3.3 实現硬件抽象扆 ………. …… 220 14. 5 BSP的实現 ··································253

12.4 位玦夏制的实現… …… …………… 220 14.5.1 Nexus One系统的


藍牙实現·····························253
第 1 章
3 元紱局域阿系统…..... …[ … ……… 223
14.5.2 Nexus S系统的
13 .l 元紱局域冏系统概述 ······· 223 藍牙实現·····························254
1 3.2 元紱局域冏子系统的結杓 223 14.5.3 GalaxyNexus系统的
13. 2.1 惡体的結杓························· 223 盔牙实現·····························255

13.2.2 wpa_su pplican t工程 225 第 15章 定位系统·········································2 7


5
13. 2.3 Wi柘本地适配庠…..……···· 227
13. 2.4 15.l 定位系统的概述……… ….………··257
WiFi的JNI部分..... …······228
15.2 定位子系统的結杓·…... ………·257
13.2.5 WiFi的Java扆· …···············228
15.2.1 惡体結杓 ·····························257
13.3 元綫局域阿BSP的結杓 229
15.2.2 JNI部分·······························259
13.3.1 仂讠文和驱劫程序· ……. ……229
15.2.3 Java部分 ·····························260
13.3.2

用户空 l 司的內容 … ..… ……·· 230
15.3 定位BSP的結杓… …...……… ···263
13.4 元鈛局域冏BSP的实現 231
15.3.1 驱劫程序 ·····························263
13.4.1 基于BCM4329 的方案
15.3. 2 硬件抽象尼 的接口 …… ········264
(Nexus One和Nexus S)·· 231
15.3.3 实瑰 硬件抽象扆………………266
13.4.2 OMAP平台的 介

15.4 定位BSP的实現……..… ... …··266


典型实現····························· 232
15.4.1 仿真器的GPS实瑰 ·········266
13.4.3 GalaxyNexus的实 瑰·… …… 234
15.4.2 Nexus One系统的实瑰……·268
第 14章 藍牙系统········································· 2 7
3 15.4.3 Nexus S系统的实瑰· …·····269

盔牙系统概述……… ..…....... …… 237 15.4.4 GalaxyNexus系统的


14.1
14.2 藍牙子系统的結杓………….…····237 实現·····································270

14.2.1 藍牙系统的結杓……… .… …23 7 第 16章 甩话系统·········································271

14. 2.2 B lueZ··································· 239


16.l 甩话系统概述… ···························271
14.2.3 bluedroi d 庠 ························· 241
16.2 屯话子系统的結杓………… ..…··271
14.2.4 藍牙的JN1部分……...…… …241
16. 2.1 惡体結杓·····························27
1
14.2.5 藍牙的Java部分 …...……···2 42
16. 2. 2 rild展 ···································27
3

. IX·
16.2.3 Java 扆中的屯话部分 275 18.2.3 Java 服各部分和
16.3 屯话 BSP 的結枸·…………… ········ 278 调用部分·····························302

16.3.1 驱劫程序 ............................. 278 18.3 背光和指示月


16.3.2 RIL 实現庠接口 BSP 部分的結杓 ……..…..… ········303
(作为硬件抽象展)….…… 280 18.3 .1 驱劫程序 ·····························303
16.4 屯话 BSP 部分的实現 281 18.3 .2 硬件抽象扆的內容………… ·304
16.4.1 R L 的參考实瑰
I ………….… 281 18.4 背光和指示灼
16.4.2 敖据连接部分…….. …… ···· 287 BSP 部分的实現………….…... … 305
16.4.3 Mock RIL ............................ 288 18.4.1 Nexus One 系统的实現· … ·305
18.4.2 Nexus S 系统的实現·…. … ·307
第17 章 警報器—实时时紳系统 290
18.4.3 Galaxy Nexus 系统的
17.1 警扳器—实时时钅中系统 290
实珗 ·····································308
17.2 警扳器一实时时钅中子
第19 章 振劫器系统..................................... 31 I
系统的結杓.................................. 290
17.2.1 惡体結杓............................. 290 19.1 振劫器系统概述………………… ····31I
17.2.2 JNI 部分.............................. 291 19.2 振劫器子系统的結杓 31 I
17.2.3 Java 部分............................. 292 19.2.1 振劫器部分的結杓··············311
I 7.2.4 Android 系统时向 19.2.2 JN I 部分······························312
方面的调用......................... 292 19.2.3 Java 框架部分 ·····················313
17.3 警扳器一实时时钅中 19.3 振劫器 BSP 部分的結杓 313
BSP 部分的結杓 …………............ 293 19.3.1 驱劫程序 ·····························313
17.3.1 RTC 驱劫程序………............ 293 19.3.2 硬件抽象扆的內容……..… ··314
17.3.2 Alarm 驱幼程序……………… 294 19.4 振劫器 BSP 部分的实珗 314
17.4 警扳器-实时时紳 19 .4.1 Nexus One 系统的实瑰… ····315
BSP 部分的实瑰…... ………….… 295 19.4.2 Nexus S 系统的实現 …........316
17.4.1 模姒器坏境中的实珗.......... 295 19.4.3 Galaxy Nexus····· …··············316
17.4.2 MSM 平台和 Nexus One
第20 章 甩池信息部分…..…..….......… ··318
系统的实瑰 ......................... 295
17.4.3 Nexus S 系统的实現……….. 297 20.1 屯池信息部分................… ···········318

17.4.4 Galaxy Nexus 系统的 20.2 屯池信息子系统的結杓 318

实現..................................... 298 20.2.1 屯池系统部分的結杓·········318


20.2.2 JNI 部分·······························319
第18 章 光系统 ............................................. 300
20.2.3 Java 部分·····························321
18.1 光系统概述.................................. 300 20.3 屯池信息 BSP 部分的結杓 321
18.2 背光和指示打子系统的 20.4 屯池信息 BSP 部分的实現 322
結杓 ............................................... 300 20.4.1 模姒器中的实瑰·…………… ··322
18.2.1 惡体結杓............................. 300 20.4.2 Nexus One····························323
18.2.2 JN I 部分.............................. 301 20.4.3 Nexus S ................................324

. X.
20.4.4 Galaxy Nexus….............. …···325 22.2.2 NFC本地庠...... …......... …····359
22.2.3 Android框架展的NFC
第21章 Android 4.x的音頻、
视頻系统·········································326 相 栄內容 ·····························360

22.2.4 NFC包·································361
21.1 Android 4.x的音颜系统.... …326 22.3 近汤通信BSP的結杓 365
21.1.1 音颜系统的結杓……………·· 326
22.3. l NFC-NCI接口····················365
21.1.2 音颜框架扆......................... 327
22.3.2 NFC接口·····························366
21.1.3 音颜BSP部分結杓············327 22.4 近汤通信BSP的实現 366
21.2 Android 4.x音颜的
22.4.1 NCI-NFC的粧实現……······366
BSP实現...................................... 330
22.4.2 NFC的 粧实現………………···366
21.2.1 主实珗和策略实珗........... 330
22.4.3 Galaxy Nexus 的
21.2.2 仿真器实瑰 .. …. …................ 330 NFC实瑰·····························367
21.2.3 A2DP实現.......................... 331
第23章 Android 4.2的屯源控制 ··368
21.2.4 Galaxy Nexus的实現·········332
21.3 Android 4.x照相机系统········336 23.1 屯源控制 ·······································368
21.3.1 照相机系统的結杓············336 23.2 屯源控制的結杓……………..……·368
21.3.2 Camera的框架展 ………… …336 2 3.2.1 惡体結杓 ·····························368
21.3.3 照相机BSP部分結杓 339 2 3.2.2 屯源控制的使用………………368
21.4 Android 4.x照相机的 23.3 屯源控制BSP的結杓 369
BSP实瑰······································342 23.4 屯源控制BSP的实瑰 369
21.4.1 仿真器实現 ·························342 23.4.1 通用的屯源控制实現 ·····369
21.4.2 Galaxy Nexus的实現...…··346 2 3.4.2 Galaxy Nexus的
21.5 Android 4.x视颜組合系统 · …349 屯源控制实現............. …······370
21.5.1 视颜組合系统結杓············ 349
第24章 本地时阅.....…......... …····················372
21.5.2 SurfaceFlinger対

视颜組合的使用 ……….……· 350 24.1 本地时冏子系统結杓 372

21.5.3 视颜組合BSP 24.1.1 本地时囘的結杓··················372


部分結杓·····························351 24.1.2 本地时阅的使用………………372

21.6 Android 4.x视颜組合的 24.2 本地时阅BSP的結杓 373

BSP实現······································352 24.3 本地时冏BSP的实現 373

21.6.1 默从实瑰 ····························· 352


第25章 Android 4.2密钳· ……... …·…………375
21.6.2 Galaxy Nexus的
25.1 密钳概述·······································375
视頻組合 ·····························352
25.2 安全和密钅月子系统結杓 376
第22章 Android 4.x近汤通信系统·······357
25.2.1 安全和密钳的惡体結杓·····376
22.1 近汤通信系统概述 ………………··357 25.2.2 keystore守护迸程…...…··376
22.2 近汤通信子系统的結杓 35 8 25.2.3 android.secu rity 的內容……·3 77
22.2.1 惡体結杓·····························358 25.3 密钅月的BSP部分的結杓 379

• XI.
25.4 密钳的BSP实現 ………….……… 380 26.4 屯源管理的策略·……... ….. ………·394
25. 4.1 通用的軟件密钳实現········ 380 26.4.1 驱劫程序的変化 ………………394
25.4.2 Gal axy Nexus的 26.4.2 用户空阅的控制………………396
密钳实瑰............................. 3g1
第27章 恢夏和升级.. …..... ……······…············397
第26章 甩源管理…..........…......................... 3g4
27. l 恢夏和升级概述... ….…….…… …397
26.1 Android屯源管理…… ….…………38 4
27.1.1 Android的Recovery
26.2 Android 內核空冏的
系统的組成.... …··················397
屯源管理······································385
27.1.2 Android的Recovery
26.2.1 惡体結杓····························· 385
系统的功能和运行流程·····398
26.2.2 wakelock······························ 386
27.2 recovery系统 …......….……... …..…·399
26.2.3 wakelock的用户空阅 ……… 388

· 26.2.4 27.2.1 編涌系统 ·····························399


earlysuspend部分………… … 389
27.2.2 init. rc脾本··························-400
26.2.5 其他····································· 39
1
27.2. 3 Recovery可执行程序和
26.3 Android 用户空向的
屯源管理······································ 392 相哭的庠·····························401

26.3.1 屯源管理的本地庠············392 27.3 Android系统交互的迥程· …····405

26.3.2 屯源管理的JNI庠·········· … 393 27.3.1 Java部分·····························40 5

26.3.3 屯源管理的Java部分······ 393 27.3.2 交互的汤景··························406

·XII•
第1章 -
Android的BSP和子系统卉友

1.1 Android板级支持工作概述

�1 .1 . 1 Android的升放源代碣工程和BSP
Android是 一 今玕放源代碼的系统, 基于Android的玕源代碼 , 可以杓建各神系统, 并
可以适配到任何支持的硬件平台上。
BSP B
( oard Support Package)的含又为板级支持包, 通常表示某介系统的特定的硬件
支持部分。 从系统結杓上 , 某一 今硬件相芙的軟件系统可以分成通用部分
和 BSP部分。 前
者与硬件元美, 后者与硬件相栄。
AOSP (Android Open-Source Project)的含又为Android 玕放源代碼 工程。Google官
方的Android 代碼和相哭內容都是升放的 , 其两站为: http://source.android.com/ 。
AOSP的大部分工程采用了Apache仂汶(Apache
License)。Apache仂汶鼓動代碼共享
和尊重原作者的著作枚, 同祥允讠午代碼修改。 Apache仂汶也是対商並皮用的友好讠午可, 使
用者 也 可以在需要时修改代碼満足需要并作 为玕源或商並严品友布或銷售。
各介 基于 Android的没各基本都是直接或向接地从 Android的升源工程荻取源代碼,
然后加入針対特定硬件的BSP部分, 由此杓建了自己的軟件部分。

�1 .1 .2 Android的系统結杓
Android系统呈然厐大 ,但是具有清晰的軟件尼次結杓。按照自下而上的 結枸,Android
的軟件系统分成4介扆次, 如囹1-1所示。
第一扆:操作系统(Linux kernel)。Android系统基于Linux操作系统,第一扆次为Linux
內核和相美驱劫。 不同的硬件平台使用不同的內核 。
第二扆: 庠 (Libraries)和运行坏境(Android Runtime)。 包括本地的各令C语言和
C++庠, 可能米自玕源工程或者 Android原生的 。 运行坏境包括C十十语言实珗的虛姒机
• O O Android板级支持与硬件相美子系统

( Dalvik Virtual Machine)和Java核心�(C oreLibraries)。


第三屄:皮用程序框架(ApplicationFramework)。Android原刨Java框架 尼由几介Java
庠組成。
第四扆: 座用程序(Applications)。 由Java 代碼、瓷源文件、工程描述文件生成的各
介单袖的皮用程序包。

Java扈i用程序展
系统API
Java框架扆
.一 ............................................、
JNI j I 核心庠 I:
Android各秤本地庠 11 Dalvik虛姒机 I:
:·..............................................
Run Time !
用户空r司 I C程序框架

內核空冏 Android素蓓
三[ 的没各驱劫

硬件系统 硬件系统

囷 1-1 Android系统的扆次結杓

从BSP玕友的角度米況, 重魚在Android系统絞下扆,Android系统欬件的运行基絀
是需要适配系统的硬件,在軟件方面主要的工作是Linll(內核中的硬件驱劫程序(第一扆),
以及Android本地扆肖中的硬件抽象扆或者适配尼(第二扆)。

1.2 Android的升友坏境和源代碼

�1.2.1 Android的升友坏境
Android的Linux玕友坏境 一 般使用Ubuntu主机。在基于Ubuntu的主机坏境中, 玕友
Android主机坏境包括以下需求: git工具、repo工具、Java的JDK、主机編洋工具等。 編
诵迥程中主要的目杯机編洋工具則使用Android升源工程中自帶的。
Android系统在編洋近程中需要編洋主机的工具, 因此需要使用主机的GCC工具鎚。
而対于編诵目杯机文件,Android在 prebuilt目汞中集成了GCC交叉編诵工具镱。
新版本Android源代碼編诵陘圭使用64位的主机坏境, 配置方式如下所示:
$ sudo··apt-get install git-core gnupg flex bison gperf build-essential\
zip curl zliblg-dev libc6-dev lib32ncurses5-dev ia32-libs\
xllproto-core母ev libxll-dev lib32readline5-dev lib32z-dev\
libgll-mesa-dev g++-multilib mingw32 tofrodos python-markdown\
li):>xml2-utiJ.s


而32位的主机玕友坏境, 配置方式如下所示:

2
第1章 Android的BSP和子系统卉友 ooe
$ sudo apt-get install git-core gnupg flex bison gperf build-essential\
zip curl zliblg-dev libc6-dev訌bncursesS-dev xllproto-core-dev\
libxll-dev libreadline6-dev libgll-mesa-dev tofrodos python-markdown\
libxml2-utils

repo是调用git封裝的工具, 安裝 repo的准各工作 如下所示:


$ mkdir -/bin
$ PATH=-/bin:$PATH

repo 工具同祥可以在Android的阿站上荻得, 方法如下所示:


$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > -/bin/repo
$ chmod a+x -/bin/repo

repo是一 介由Google提供的工具, 本原上是 介朐本, 它可能隨时更新, 因此皮圭从
Google的相皮地址荻得 最新的repo, 夏制到本地之后更改枚限米使用。

�1.2.2 源代碚合庠
AOSP工程的源代碼放置在Android的源代碼合庠中, 其冏站为:
https://android.googlesource.corn/
迏令 阿址既可以作为使用 git 工具荻取和提交源代碼的地方, 也可以通逍剖寛器使用

https仂汶访冏, 由此荻得 源代碼合庠的 些信息。

1. repo荻取全部工程

直接使用repo荻取Android完全的源代碼方法包括初始化 代碼合庠 和荻取代碼两今步


骕, 每介步驟可以增加不同的參數。

使用repo 初始化 Android的代碼合庠的 般方法如下所示:
$_ repo 虹!1it -u https://android.goog_lesource.com/platform/manifest

由于没有使用額外的參敖, 此时得到的是代碼合庠中 master分支(主分支)的最新 版


本。 在初始化 迥程中, 也可以看到列出的各令分支(branch) 和杯签(tag)的名稔, 迏些
就是可以在初始化的迥程中 使用-b指定的參數。
在repo init时, 使用-b迭項下裁稔定的Android 2.33
. 版本:

在repo init时, 使用-b造璜下裁Android 4.x的版本:


$ repo in_:j,_t -u httl)s://android. google§._ource.com/platform/manife紅t_-.,!:i android一4退.4_r2

repo init之后, 将生成險藏目呆repo, 其中文件 .repo/manifest.xml为 repo 工程的描述


文件, 表示repo时包含的各介工程, 其中的几今糸目如下所示:
<project path= "fr-ameworks/bas护 玉a:me=-"platform/frameworks/base" />
<project path= "hardware/libhardware" name= "platform/hardware/libhardware" />
<project path= "hardware/libhardware_legacy"
name= "platform/hardware/libhardware--�_
legacy" ../.2'

repo/manifest.xml 中的 path表示工程荻取后的路往(基于者前目汞), name表示工程

3 '
• O O Android板级支持与硬件相美子系统

的原始名稔。
在綬迥repo血t之后, 可以使用repo荻取Android的全部代碼, 方法如下所示:
$ repo sync

使用repo sync时,也可以同步 一介单介工程的內容,需要使用工程的名稔作为repo sync


的參敷, 工程的名杯可以从manifest.xml荻得。 荻得单介工程的方法如下所示:
$_ repo sync { pro;t_ect_r1_ame)

荻取工程后,每介工程的目汞中含有 令git 目泵,迏就是工程 版本管理目呆 ,迏些.git
目汞中的內容大部分是到.repo/projects/<project_path >的连接。
从BSP升友的角度, Android 源代碼工程中, 几介重 ,亞的目呆如下。

�frameworks/base: 核心框架部分, 包括C丑和Java的框架。
• hardware/libhardware: 硬件相美內容, 包括硬件模玦。
• hardware/libhardware_legacy: 陈汨的硬件相美內容, 有些內容依然在使用。
• hardware/<vendor> /: 包括qcom和ti等「商付対自己硬件的內容。
• device/*/<TA RGET_PRODUCT> : 各令目才示板的配置目汞 。

2. 其他工程
Android 的升源代碼包括了很多工程 , 使用repo并非得到 所有的工程, 而是使用一介
特定的工程列表。 可以直接使用git 工具荻取其他工程:
$ git clone https: / /android. goo9lesource. com/name

工程的名字可以从https://android.googlesource.com/荻取。
例如, 荻得repo 工具的方法如下所示:

命令执行完成后, 将荻得名稔为 repo的目呆, 其中 包含git 目汞用于存放工程的版本


控制信息。 可以迸入并査看其中的內容:
$ cd repo
$ git branch -r
origin/HEAD -> origin/master
origin/maint
origin/master
orig__:i,n/stable

git branch -r列出了远端的分支名稔, 迸一 步使用git checkout可以取出某介穏定 分支


的代碼。

些与BSP升友相美的工程如下所示。
• kernel/*: 各介不同系统的Linux 內核的源代碼。
• platform/prebuilts/gcc/*: 各秤交叉編洋工具镱(包括不同版本和体系結杓)。
• toolchain/*: 主机自身的一些工具 。

' 4
第1章 Android 的 BSP 和子系统玕友 ooe

1.3 BSP模坎和相美子系统

�1.3.1 Android 的 BSP

1. 主要工作
BSP 的支持工作实际上是不同硬件的适配工作。 Android 系统的 BSP 部分的杓建工作
的核心也就是 Android 系统的硬件平台的移植工作。 Android 系统的移植工作的目的是为了
在特定的硬件上运行 Android 系统。
`` "
BSP 的本意是板级支持包, 但対于 Android 系统, BSP 部分并非以 包 的形式存在,
而是一部分代碼改幼。 Android 系统的 BSP 部分其实分布于 Android 玕源代碼的不同路往
中, 有些 BSP 的支持工作甚至不是狓立的文件。
BSP 的杓建主要有两介部分的工作:
• Linux 內核中硬件相美部分(主要是各神硬件的驱劫程序)。
• Android 用户空阅的硬件抽象扆 (HAL, Hardware Abstract Layer) 。
Linux 中的驱劫工作在內核空岡, Android 系统硬件抽象尼工作在用户空阅, 有了迏两
介部分的結合, 就可以辻厐大的 Android 系统运行在特定的硬件平台上。
在具有了特定的硬件系统之后, 通常在 Linux 中需要实珗其驱劫程序, 迏些驱劫程序
通常是 Linux 的杯准驱劫程序,在 Android 平台和其他 Linux 平台基本上是相同的。主要的
实瑰方面是 Android 系统中的硬件抽象尼, 硬件抽象扆対下调用 Linux 中的驱劫程序, 対
上提供接口, 以供 Android 系统的其他部分(通常为 Android 本地框架扆)调用。
Android 的硬件抽象扆的接口是本地移植尼的接口,不厲于系统杯准化 API, 不具有向
前或者向后兼容性。髄着 Android 版本的演迸, Android 的硬件抽象扆的接口結杓也在笈生
変化。 対于同 一介硬件没各, 为了适陘不同版本的 Android 系统, 其 BSP 部分通常需要做
出修改甚至重写, 此时没各驱劫程序可以重用, 硬件抽象尼則需要重写。

2. Android 的BSP主要內容

Android 的 BSP 工作不是 瑣完整的工作, 除了基本系统的支持之外, 主要工作是基
本平行的若干介部分。 迏些 BSP 部分基本袖立, 其中也存在少量交互, 大部分內容都与硬
件有美系。
Android 源代碼工程中具有很多子系统, 但并不是每 一 令部件都需要做 BSP 的适配,
需要 BSP 没各的部分通常是与原始硬件相美的部分。
移植需要的工作往往可以和实际的硬件严生対皮美系, 例如晁示屏、 鍵盎、 喇叭、 掇
像失、 盔牙、 元綫局域阿、 GPS、 加速度亻刮感器等都是典型需要 BSP 支持的部分。
対于一 些純軟件的部分, 不需要 BSP 的特殊支持。 例如敖据庠、 算法、 字体外理和
XML 解析晁然和硬件元美, 也不需要 BSP 的特殊支持。


対于一些中阅尼的部件, 呈然与硬件有美, 但本身是通用部分, 也不需要 BSP 的特殊

5
• O O Android 板级支持与硬件相美子系统

支持。 例如冏絡仂汶枝、 藍牙仂汶枝、 文件系统, 它亻i'J呈然下扆需要硬件, 但是本身与硬


件差昇元美, 在任何 一 令硬件平台都是相同的, 不需要做特殊支持。
BSP 的工作和核心座垓是原始硬件的底扆。 例如盔牙耳机、 藍牙伟榆文件、 藍牙聊天
等程序最終依賴的硬件是藍牙; 照相机、 掇像机、 糸形碼讠只別等程序都依賴硬件, 其底扆
対皮的实际硬件晁然都是掇像失部分; 自劫銬屏、 晃劫屏幕控制的各秤游戏, 都同祥依賴
于加速度亻考感器。

�1.3.2 BSP和硬件相夫子系统
BSP 部分呈然在实际的工作中通常只包括驱劫程序和硬件抽象尼,但是在枸建 BSP 的
近程中通常需要栄注与硬件相美的子系统。 可以況各令 BSP 部分是 Android 各介硬件相芙
子系统的下尼。 迏些与硬件相美的子系统分成了几介方面。

1. 基本系统部分
Android 系统运行的基絀是具有可运行的 Linux 內核和基本的文件系统,为了调试方便
坯需要具有串口或 USB 等。 基本 Linux 的支持的实瑰部分主要是在內核空阅中的工作, 在
用户空向的工作也有差別, 但是主要在于简单的配置。

2. 用户交互界面部分
用户交互界面是 Android 系统运行最直戏的部分, 包括晁示和用户榆入两令部分。 晁
示部分是屏幕上呈現的內容, 通常基于 LCD 等硬件和 LCD 控制器。 用户榆入部分則包括
鍵盎、 蝕摸屏、 孰迹球、 鼠杯等没各。

3. 多媒体榆入l榆出部分
多媒体的諭入/諭出部分包括了视颜和音颜两秤, 排列組合起米 一 共是4秤情況。 音颜
的榆出和榆入坏节比絞奕似, 作为狓立的音颜系统杓建。 视颜的榆入坏节就是掇像�, 奐
靑从硬件中荻取视颜幀。 视颜的諭出坏节最終的硬件就是屏幕, 但是为了性能优化, 通常
LCD 控制器具有特殊功能实現视颜的疊加扆。

4. 加速部分
加速部分通常由特殊的片內硬件模玦完成垓部分功能,目的是辻系统荻得更好的性能。
加速部分通常是可造的,在没有的情況下由欬件完成相陘的功能。 Android 系统的加速部分
包括 OpenGL3D 加速坏节、音视颜的編解碼部分、囷像的編解碼部分、囹像的灶理部分等。

5. 甩话部分
屯话部分是移幼屯话系统的核心, 包括屯话、 短信和數据连接功能。 屯话部分是一 令
組合的模玦, 由屯话模玦提供功能, 屯话模玦的实現方式有多科。

6. 连接部分
连接部分 (Connectivity) 通常指盔牙、 元紱局域冏 (WiFi) 和 GPS 定位系统等方面。


藍牙硬件対上扆可以支持多秤功能, 元紱局域阿提供冏絡的功能, GPS 則提供定位功能。

6
第1章 Android的BSP和子系统卉友 ooe

近距窩元紱通信技木(Nea rFie ld C ommunicat ion, NFC) 也是连接部分的 一 秤。

7. 伟感器部分
伟感器包括了多秤从外部荻取信息的模玦, 包括加速度伟感器、 方向伟感器、 温度亻夸
感器等多秤硬件。 由于亻考感器在荻取信息方面的共性, 在Android 中各秤伟感器被綜合为
BSP支持的 一 介方面。

8. 其他部分
系统的 一 些附厲部分也需要 特定的BSP支持, 例如, 用于背光和指示打的 部分, 手机
振劫器部分、 屯池信息部分、 实时时紳部分等。

�1.3.3 不同哭型的Android没各
Android系统最初是为了支持移劫没各(手机)而建立的 。统逍多年友展之后, Android
己經不仅仅是一 令移幼没各的平台, 也可以用于其他突型的没各, 目前Android主要适用
的没各包括移幼屯话(Mobile)、 平板屯胭(Ta blet)、 上两本(NetBook)、 屯视、 机璜盒、
汽牢屯子等。
在各秤基于Android的没各中, 通常移幼屯话需要做全部的BSP支持。 其他突型的没
各需要做出絞少的BSP支持內容。 例如, 非屯话奕的没各通常不需要屯话系统;屯视、 机
预盒等不需要振劫器、 伟感器等部分;固定位置的没各則不需要实現GPS系统。BSP支持
的 多少取決于没各自身的用途。
各神Android没各也可以具有特有的硬件, 迏些特有的硬件在Android玕源工程中没
有対皮的內容, 为了支持它亻[], 可以仿照Android原有的子系统及BSP部分的結杓米自行
实瑰。

7 '
第2章
Android系统BSP部分工作

2.1 Android的BSP部分 I 作概述

Android的BSP 最基絀的工作是Android最基本系统的运行,主要包括基本Linux系统
支持(CPU、 內存、 定时器)及串口、 存儲器等基本没各的使能。
Android的BSP部分在用户空冏也 包括了 一 些全局方面的工作。 例如, 針対于某令硬
件平台的板级內容的定又、 不同于默从的硬件配置、 特定的初始化流程、 特定的没各节煮
管理等。
Android的BSP部分的工作主要需要在不同的子系统中实施, 根据子系统和实际需求
的不同, 所需要迸行的工作也不同。
対于大部分子系统, 需要实瑰Linux的没各驱劫程序和用户空阅的硬件抽象居。 因为
从原則上釆悅, 硬件抽象扆是 Android通用部分和硬件相美部分的接口, 但是作为与硬件
相美 的部分, 又必須具有Linux中的驱劫程序才能工作。 佳感器部分、 音颜部分、 视颜榆
出部分、 掇像染部分、 屯话部分等都厲于此秤情況。

也有 些子系统在用户空冏的硬件抽象尼或适配尼已經是 杯准化的 , 在不同的 系统中
元颎重新实現。 対于迏秤子系统, 只需要实現Linux內核中的驱劫程序即可。 榆入部分、
元紱局域阿部分、 藍牙部分、 振劫器部分等都厲于此秤情況。 対于有杯准的硬件抽象扆的
系统, 有时 也 需要做 一 些配置工作。

2.2 BSP的全局部分

BSP的全局部分与子系统元矢, 而是 在Android玕源工程中的整体改劫 , 呈然Android


升源工程的大部分公共代碼与硬件元哭, 但坯是有一部分內容与硬件相栄, 迏就是BSP的
全局部分。
第2章 Android系统 BSP 部分工作 ooe

�2.2.1 源代碣工程板级別支持部分
Android系统通迥不同的配置米实瑰同一套源代碼(或者巨別不大的源代碼)対不同板
的支持。事实上, 支持各秤 Android没各的源代碼都是从Android玕源工程衍生而來的。
device/*/<board>/和vendor/*/<board>/为Android 源代碼工程中每介板的配置目汞,迏里的*
'`
表示「商的名稔 , 例如HTC、 SAMSUNG 等, 其下一 级目呆則为 板" 的名杯。

1. 板级造搔
在Android系统編涌之前, 首先需要确定的就是名稔为TARGET _PRODUCT 的坏境
変量, 垓変量取值的方法有多秤。Android推荐的方法是在envsetup.sh之后通迥lunch迸
行没置。
在編诵之前, 可以 使用envsetup.sh朐本迸行 編洋的配置, 配置的近程中, 将佘调用每
一介板的vendersetup.sh文件。
Android 2.3版本的坏境没置迥程如下所示:
$ • build/envsetup.sh
including device/htc/passion/vendorsetup.sh
including device/samsung/crespo/vendorsetup.sh

Android4.x版本的坏境没置這程如下所示(有省略):
$ • build/envsetup.sh
including device/samsung/maguro/vendorsetup.sh
including device/samsung/tuna/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
includin9�k/bash_completionj adb.bash
'` 一
以上的passion、crespo、maguro 和tuna都是 板"的名杯。配置的迥程是 介"遍历"
的近程, 将佘调用device///和vendor///目呆中每 一介板的vendersetup.sh文件。只要板级支
持目呆中有vendersetup.sh 文件 , 就令被调用。vendersetup.sh被调用后实际上将增加系统
可以造拌的目杯严品。
envsetup.sh朐本执行 后, 各今板在使用lunch命令迸行造掙严品时有所体現。
Android 2.3可以造掙的內容如下所示:
$ lunch
You're building on Linux
Lunch menu... pick a combo:
1. generic-eng
2. simulator
3. full_passion-userdebug
4. full_crespo-userdebug
Which would_y鎰J lik�? [generic-eng]

Android4.x可以迭掙的內容如下所示:
$ lunch
You're building on Linux
Lunch menu... pick a combo:
1. full-eng


2. full_x86-eng

9
• O O Android板级支持与硬件相美子系统

3. vbox_x86-eng
4. full_maguro-userdebug
5. full_tuna-userdebug
6. full_panda-eng
Which would _y箜!._l�tl.._[ [_ull-eng J

在lunch时出瑰的菜单瑣目中, 出現了几令 造項, 可以使用數字迸行造掙。 从中可見,


除了默从的generic-eng等造瑣之外, Android 2.3的full_passion-userdebug和full_maguro­
userdebug、Android 4.x的full_maguro-userdebug和full_panda-eng等造項就是米自于自定又
板级支持的文件 。
用于Nexus S手机的device/samsung/crespo/vendersetup.sh 文件如下所示:

用于Galaxy Nexus手机的device/samsung/maguro/vendorsetup.sh 文件如下所示:

其中 定又 TARGET _PRODUC T 宏的名秣。 执行lunch 命令, 并造捅了數字后, 实际上


得到的結果也是没置TARGET_PRODUC T等宏 。

提示: Nexus One使用passion为板名, 另一今名林为mahimahi; Nexus S使用crespo


为板名,另一今名林为herring; Galaxy Nexus以maguro为板名,它与panda 板都厲于tuna。

2. 板级配置

BoardConfig.mk是 介平台全局配置的文件, 只是迸行配置的定又, 不能包含实际执
行的命令 。 BoardConfig.mk文件 配置的內容可以在各介工程的Android.mk文件中得到 。其
中主要包括了 一 些平台 配置変量的取值。
几介定又目柝平台的配置変量如下所示。
eTARGET_CPU_ABI和 TARGET_CPU_ABI2: 目杯 CPU的ABI(Application Binary
Interface)。取值有以下几令,armeabi表示ARMv5体系結杓; armeabi-v7a表示ARMv7
体系結杓(Cortex-A); x86表示 x86体系結杓 。
e TARGET_ARCH_VARIAN T: 目杯体系結枸的額外没置, 取值 armv5 te表示ARMv5
体系結枸, armv7-a-neon表示針対支持 NEON的ARMv7体系結杓, x86-atom表示
ATOM的x86体系結杓。
eTARGET_CPU_SMP: CPU対杯多灶理器的支持, 取值为true或false。
e ARCH_ARM_HAVE_ TLS_REGIS TER: ARM灶理器的 TLS 支持, TLS 的含又为

Thread Local Storage, 是 秤特殊的寄存器, 取值为true或false。
eTARGET_BOARD_PLATFORM : 目才示板平台的名稔, 取值为一 介字符串。
eTARGET_NO_BOO TLOADER: 元BootLoader, 取值为true或false。
eTARGET_NO_KERNEL: 元內核, 取值为true或false。
e TARGET_NO_RADIOIMAGE: 元Radio部分的映像, 取值为true或false。
如果是編诵默 从 的仿真器支 持 , 就 是元 BootLoader 且元 內 核 的, 因为不需要

�10
第2章 Android 系统 BSP 部分工作 ooe

BootLoader, 且使用的是预編诵的內核映像(相岂于 zlmage) 。


与硬件相美子系统的平台配置変量如下所示。
e DEFAULT_FB_NUM: 默 iA framebuffer 的致目, 取值一 般为 l 、 2 或 4 等敖值。
e BOA吣_USES_GENERIC_AUDIO: 是否使用通用音頻模玦, 取值为 true 或 false。
e USE_CAMERA_STUB: 是否使用照相机的粧实瑰, 取值为 true 或 false。
e BOARD_EGL_CFG: EGL 的配置文件, 取值为 一令目杯机上的路往。
a BOARD_USES_HGL: 是否使用硬件的 OpenGL, 取值为 true 或 false。
a USE_OPENGL_RENDERER: 是否使用 OpenGL 的渲染器, 取值为 true 或 false。
e BOARD_USES_OVERLAY: 是否使用视颜疊加扆, 取值为 true 或 false。
e BOARD_HAVE_BLUETOOTH 等宏: 是否具有盔牙, 取值为 true 或 false。
e WIFI_DRIVER_*: 泗1相美的驱劫路往, 取值为主机的目杲。
除此之外, BOARD_NAND_PAGE_SIZE 宏表 示 板 上 的 Nand Flash 的頁大 小 ,
BOARD_FLASH_BLOCK_SIZE 表示板上的 Flash 的坎大小。

�2.2.2 硬件相夫的代碼改劫

1. 文件系统配置
Android 的源代碼统迥編诵后, 将生成目才示系统的根文件系统 Croot) 、 主文件系统
(system) 和數据文件系统 (userdata) 的目呆, 然后生成几令映像。 根据 Linux 文件系统的
特煮, 每今文件具有自己的用户 ID Cuid) 、 組 ID Cgid) 以及 3x3 的枚限, 也就是本用户、
本組用户、 其他用户三者和埃 Cr)、 写 Cw)、 执行 (x) 三者的排列組合。
源代碼工程的 system/core/include/private 目呆中的 android_filesystem_config.h 定又了
Android 用户 ID、 組 ID 和定级目汞的厲性等。
美于 ID 定叉的內容如下所示:
#define
fdefine
AID_ROOT
AID_SYSTEM

1000
// UNIX的根用户
II系统用户
tdefine AID_RADIO 1001 II屯话子系统(RIL)
#define AID_BLUETOOTH 1002 刀盔牙子系统
#define AID_GRAPHICS 1003 //囹形没各

此姓的 id 都是整數, 在 Android 系统中用户 ID Cuid) 和組 ID (gid) 使用取值相同,


也就是 1000 表示用户 ID 的时候是系统用户, 表示組 ID Cgid) 和所厲組 (GROPUS) 的时
候也是系统用户。 其中 AID_ROOT 的取值为 0, 迏是遵从 UNIX 系统的司愤, 以 0 作为根
用户的 ID。
每介 ID 为整數突型, 都有 一令字符串与之相対戍, 相美的結杓如下所示:
struct android_id_info { const char *name; unsigned aid; }; //名杯<->這
static const struct android_id_info android_ids[] = {
{ "root", AID ROOT, I,
{ "system", AID "SYSTEM, },
//省略部分內容


};

11
•oo Android板级支持与硬件相美子系统

迏些字符串是在 Android 系统的終端中使用Is、 Ps等命令査看时晁示的名杯。


struct fs _path_config表示了 文件系统路往的厲性 的敖据結杓, 如下所示:
struct fs_path_config {
unsigned mode; II模式
unsigned uid; II用户ID
unsigned gid; II組ID
const char *prefix; II目汞前綴
};

android_山rs和android_files是fs_path_config炎型的两令數組,它亻I']分別表示了目呆和
具体文件的厲性。
fs _path_config奕型的android_dirs數組中定又如下所示:
static struct fs_path_config android_dirs[] = {
{ 00770, AID_SYSTEM, AID_CACHE, "cache" } ,
{ 00771, AID_SYSTEM, AID_SYSTEM, "data/app" },
{ 00771, AID_SYSTEM, AID_SYSTEM, "data/app-private" },
{ 00771, AID_SYSTEM, AID_SYSTEM, "data/dalv坏-cache" } ,
{ 00771, AID_SYSTEM, AID_SYSTEM, "data/data" } ,
(00771, AID_SYSTEM, AID一_SYSTEM, "data" },
{ 00750, AID_ROOT, AID_SHELL, "sbin"),
{ 00755, AID ROOT, AID_SHELL, "system/bin" },
II省略部分內容
{ 00750, AID_ROOT, AID_SHELL, "init*" } ,
{ 00644, AID_ROOT, AID_ROOT, 0 },

fs _path_config 炎型的android_files數組中定又如下所示:
static struct fs_path_config android_files[J = {
{ 00440, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.rc"),
{ 00550, AID_ROOT, AID_SHELL, "system/etc/init.goldfish.sh" },
[ 00440, AID ROOT, AID_SHELL, "system/etc/init.trout.re" ) ,
II省略部分內容
};

例如,根据以上定又,data目汞 用户和組都是system, 访何枚限为00771, 也就是system


用户和厲于 system 組的用户可渙、 可写也可执行 , 其他情況只可执行。 使用ls -l査看data
目汞 在文件系统中的情況如下所示:
drwxrwx--x systeJ!l.---R:tsJ:em

如果需要更改文件系统路往的厲性等內容, 需要更改android_filesystem_config.h 文件
即可。rnkyaffs2image等工具在生成文件系统映像时,将佘根据其中的內容配置文件系统的
各令目汞和文件的厲性。

2. init.rc腮本

init.rc 胸本是Android系统的第 介迸程init执行 的胸本,奐靑完成初始化的工作。init.rc

是 介具有自定叉语法 的文本 文件, 主要包括Command (命令)、 Action (劫作)、 Trigger
(蝕岌)、 Service (服各)、 Option (造項) 和Property (厲性) 等几秤语法。
init.rc朐本位于根目汞中, 朐本被解析后将放入臥列执行。 init.rc朐本通常有两介: 第

, 12
第2章 Android系统BSP部分工作 ooe

I -t-是init.rc, 第 2 介是init.< 没各>.re, 作为硬件相美的初始化启劫內容。 两介init.rc脾本


的语法和作用完全相同, 只是前者用于通用的工作, 后者用于没各相美的工作。
默从的init.rc位于system/core/root血/目汞中,在Android默·从的仿真器坏境中, 使用
的第2介init.rc腮本的文件名杯为init.goldfish.rc, 其源代碼的路往 为system/core/rootdir/etc/。
init.rc 胭本 执行的几介阶段如下所示:
on early-init § 早于init阶段执行的內容
on init t init阶段执行的內容
on fs § 挂接文件系统执行的內容
on post-fs #挂接文件系统之后执行的內容
on boot #启劫时执行的內容

在执行颜序上,init的几介执行 阶段高于胭本 。 也就是況, 执行 第一介init.rc的on init


之后, 再执行 第二介init.rc的on init的內容, 然后 是第一令的on fs的內容, 依此癸推。
几介常用的命令如下所示:
export <name><value> #尋出坏境変噩
exec <path> [<argument>] #执行可执行程序
write <path><string> [ <string>]** #写某介文件
mkdir <path> [mode] [owner] [group] #建立目汞
insmod 圣�th> #插入某介模炔

默iA init.rc朐本中, 挂接文件系统的片段如下所示:


mount yaffs2 mtd@system /system
mount yaffs2 mtd@system /system ro remount
mount yaffs2 mtd@userdata /data nosuid nodev
ffs2 mtd@cache /cache nosuid nodev

以上 语句用于使用YAFFS2格式的 Flash 文件系统的情況下,表示将 Flash存儲器的


system分巨,挂接到/system目汞 ,并置为只涙;将userdata分巨,挂接到/data目泵;将cache
分巨, 挂接到/cache目泵 。
两介与没各相美的蝕笈糸件如下所示:
on device-added-<path> #没夤增加到某介路往的情況
on c!evice =removed-<!)ath> #没各从某介路往刪除的情況

根据迏介语法可以決定,在某令 路往的没各出現或者消失之后, 执行 某介劫作。


服各表示启劫一介可执行程序,迭項是服各的附加內容,用于配合服各 使用 。 一介硬
件相美的服各(盔牙)的片段如下所示:
service bluetoothd /system/bin/bluetoothd -n
socket bluetooth stream 660 bluetooth bluetooth
socket dbus bluetooth stream 660 bluetooth bluetooth
group bluetooth net_bt_admin misc
disabled

以上 內容运行的是/system/bin/bluetoothd 可执行程序, 作为 bluetoothd 服各 ,指定其厲


于bluetooth、 net_bt_admin等几介組(GROUPS)。 Socket定叉了与之配套的套接字文件,
它亻i'J被放置 到/dev/socket/目泵中。

13 '
•oo Android板级支持与硬件相美子系统

3. ueventd.rc腮本
ueventd. rc胸本是被奐靑没各管理的ueventd程序使用的。ueventd程序 在初始化的絞早
阶段运行, 一般为继init 之后的用户空阿的第2介迸程。 対于在系统中的没各, 在初始化
和熱插拔的时候, 內核 将有uevent消息送到 用户空冏,ueventd守护迸程就将利用 迏秤消息
迸行 没各节熹(/dev/中的文件) 的建立和刪除。 其 功能奕似于桌面 Linux的udev。
ueventd.rc朐本作 为其中 没各管理的列表, 与 init .rc癸似,ueventd实际上也有两介, 一
今是名稔为ueventd.rc的主没各朐本,另 一令是名稔为ueventd.< 没各>.re的具体硬件的脾本。
二者的功能和语法也是相同的。
ueventd 朐 本的默从路往为 syst ern/c o re / ro otdi r/ueventd.rc 。 在仿 真 器坏境中 ,
ueventd.goldfish.rc 的內容为空。
ueventd.rc朐本将被安裝到目才示系统根目杲,負靑定制/dev/中的没各节熹等內容。 其 內
容片段如下所示:
/dev/null 0666 root root
/dev/ashmem 0666 root root
/dev/alarm 0664 system radio
/dev/eac 0660 root audio
/dev/graphics// 0660 root graphics
/dev/ttyMSMO 0600 bluetooth bluetooth

対于没各节燕定叉的是没各节煮名稔、 枚限、 用户 名和組名 等內容。 例如, 以上的没


各节燕均为字符没各: /dev/null 是杯准 Linux中 一介硬件元美的没各(主没各弓为1, 次没
各另为3); /dev/ashmem 是 Android 的 Linux 中 一介硬件元美的没各(主没各另为 10的
MISC没各); /dev/ ea c是Android 仿真器中模姒佽品的没各, /dev/ttyMSMO是高通平台中
的串口没各; /dev/graphi cs /則是針対 用于晟示的幀緩沖没各(主没各弓为21), Android対
其更改了位置。 它亻f]与硬件或者 相美, 或者元美, 但实現部分均为 Linux 內核中的驱劫程
序, 在迏里的配置是为了自劫 建立其在用户空阅的没各节熹。

2.3 Android的Linux操作系统

Android 是基于Linux操作系统的,Linux操作系统包括Linux 內核和驱劫程序 。 Linux


操作系统位于Android軟件系统的最低的一介尼次。
Android系统目前可以支持ARM、 x86平台, 迏些平台之阅的可移植性是由Linux操
作系统的移植性來实現的。 Android系统 基本上使用的是柝准的Linux2.6內核, 与 其他嵌
入式平台的Linux系统奕似 。

�2.3.1 Android中的Linux操作系统的特定內容
Android中使用 Linux操作系统, 主体部分是Linux的通 用代碼。除此之外, 主要包含
三令方面的內容。
0板级移植

�14
第2章 Android系统BSP部分工作 ooe

a Android的內核組件
暈各秤硬件的驱劫程序
在Android的Linux操作系统的3今方面中,板级移植是対一 介硬件平台最基絀的Linux
支持;Android的內核組件包括考用的內核模玦和驱劫程序,它亻[]并非杯准Linux中的內容,
却是Androi d 系统的 Linux中通用的內容;各秤硬件没各驱劫程序則是每 一 介硬件模玦所
需要的驱劫程序。
Android系统通常用于移劫没各或者其他的嵌入式没各,因此多基于ARM体系結杓,
在 ARM 体系結杓中具有多神 灶理器。対于同 一 秤 灶理器,不同的外围没各,也可能将使
用不同的驱劫程序。如困2-1所示为Android中的Linux內核与驱幼。

系统调用接口(S}'.stem Call)

I I
Linux內核
迸程调度kernel 內存管理mm 冏絡net

I I 虛姒文件系统vfs

「一一一一一一一一丁-一一一一三三二-一-一l
迸程通信ipc 驱緝程序driver
各神文件系统

I
各秤硬件
的驱劫程序

l---一一---------·......................··--··........................................
胆2-1 Android中的Linux內核与驱幼

�2.3.2 Android的Linux的基本支持
Android的Linux的基本支持包括 板级移植部分和Android的內核組件部分。迏两介部
分完成后,基本的Android系统即可运行。

1. 板级移植部分
从Linux系统的角度,板级移植的核心部分位于arch/<arch>/< mach>目汞中。<arch>表
示的是 一神体系結杓,例如arm、x86等。 < mach>表示的是 一 秤机器或者硬件平台,例如
mach-ms m 対座高通的MSM平台的机器,mach-omap2対皮德州仅器的OM战平台的机器,
mach-goldfish則是Android仿真器使用 goldfish灶理器。此部分的移植通常要实現名稔为
struct machine_ desc的結杓体,其中包括了內存、定时器、中端等几部分 操作系统运行最基
絀的內容,需要各介不同的硬件单狹实現。
除此之外,为了辻 一 介基本的 Android 系统可以运行,坯需要具有文件系统和基本的
调试手段。文件系统通常杓建于Flash等存儲器上,可以使用yaffs或者 ext 等文件系统。

15�
•oo Android 板级支持与硬件相美子系统

基本的调试手段可以使用串口或者 USB 没各, 它亻I']通常使用 Linux 中柝准的实現方式。


対于一 令特定的硬件平台, 如果是新杓建, 則需要做 Linux 的板级移植部分。 如果是
从其他平台移植到 Android 中, 則不需要做板级移植部分。

2. Android 的內核組件

Android 的內核組件是 Android 为 Linux 操作系统增加的內容, 它亻I']与硬件元美。 迏些


內核組件大部分以驱劫程序的形式存在, 各介模玦相対狓立。 屯源管理則作为 一 令全局的
部分存在。 Android 的內核組件可以从 Android 的 Linux 內核的通用代碼中得到, 它亻I']的源
代碼大都以秧立的文件, 在 KConfig 和 Makefile 中有相美的璜目。 Android 內核組件中的
驱劫程序被放置到如vers/staging/android/目汞中, 屯源管理的部分在 kernel/power/目呆中,
另有 一 些分散于源代碼各令部分的实瑰內容和対皮的染文件。
対于支持 Android 的各秤硬件平台, 都需要迏些 Android 的內核組件, 并且各介不同
的 Android 没各所使用的迏部分內容也是相同的。 対于已經完成了 Linux 部分支持、 但是
不支持 Android 的平台, 杓建 Android 的內核組件是主要的工作。 Android 的內核組件方面
的工作实际上只是搬移相同的代碼, 不厲于真正的硬件适配(或稔移植)的工作。

�2.3.3 Android各今硬件没各的驱劫程序
Android 系统各介硬件没各的驱劫程序, 就是 Linux 操作系统的驱劫程序。 Linux 的驱
劫有其固有的写法, Android 系统中的硬件没各的驱幼程序也 一定遵从迏些写法,可以使用
字符没各、 玦没各、 两絡没各、 sys 文件系统、 proc 文件系统作为內核空阅和用户空向的接
口。用户空伺通迥內核空向中的驱劫程序完成対硬件的访阅和控制。Android 中各秤硬件没
各的驱劫程序是 Android 系统 BSP 支持的重煮。
迏些驱劫程序与 Android 系统有美系, 因此它亻I']实际上是 Android 各介硬件相美的子
系统和具体的各秤硬件的中冏扆。 根据 Android 各令硬件相美的子系统的自身移植扆及在
Linux 內核中驱劫的框架的成熟度, 分成了3神不同情況。
第 一 神情況是完全使用 Linux 中杯准的驱劫架枸。 迏秤情況的特燕是驱劫程序框架在
Linux 中已經成熟而杯准化, Android 硬件相美的子系统直接使用迏秤驱劫程序作为某令子
系统所使用的驱劫程序。
迏秤情況的例子包括:用户諭入系统使用 input 驱幼程序的字符没各节燕,尤其是其中
的 event 没各;元鈛局域冏子系统使用 wlan 驱劫提供的阿絡没各,盔牙子系统使用 bluetooth
提供的阿絡没各; 屯池信息系统使用內核中 Power Supply 模玦提供的 sys 文件系统接口。
第二牙中情況是基本使用 Linux 中杯准的驱幼架杓, 但是也可以造拌其他形式的驱劫。
迏秤情況是 Linux 中已經有程序的驱劫程序,Android 硬件相美的子系统可以支持迏神情劫
程序, 但是又給了实現者造掙的机佘。 此时, 不同驱劫的差昇需要依靠硬件抽象扆米迸行
屏蔽。
迏秤情況的例子包括: 晁示子系统 一 般使用杯准幀緩沖 (FrameBufier) 驱劫程序的字
符没各节熹, 但是也可以使用変化的幀緩沖驱劫, 或者其他驱劫程序;音颜子系统可以使
用杯准的 oss 或者 ALSA 驱劫程序的各介字符没各节燕,也可以使用其他形式的驱劫;照

, 16
第2章 Android系统BSP部分工作 ooe

相机子系统一般使用杯准V4L2驱幼作为字符没各节燕, 但是可以使用変化的V4L2驱劫
或其他驱劫配合V4L2驱幼, 或完全的其他驱劫;背光和指示打子系统使用led驱劫程序提
供sys文件系统作为接口, 但也可以有其他的形式; 定位系统和屯话則通常使用內核中的
tty驱劫的字符没各节燕, 但也有其他的实瑰方式。
第三秤情況是 使用各秤自定叉的驱劫程序。 対于某些模玦, 杯准Linux內核没有钳対
的驱劫程序框架。 因此Android硬件相美的子系统不需要遵从任何杯准, 使用各秤能实現
功能的驱幼均可, 由硬件抽象展作为対上扆的接口, 屏蔽不同驱劫之伺的差昇。
迏神情況的例子包括:伟感器子系统可以使用原始字符没各、 MISC字符没各或者sys
文件系统作为驱幼的接口; OpenGL和多媒体編解碼加速部分可以根据具体平台的情況使
用各神炎型的驱劫程序。

2.4 Android的硬件抽象居

�2.4.1 硬件抽象屋的地位和功能
硬件抽象尼是位于用户空岡的Android系统和位于內核空冏的Linux驱幼程序中向的
一介扆次。Android中硬件抽象尼的結杓如囹2-2所示。

Android Java成用扆

Android Java框架展

Android本地框架展

�三三�--一門言言/三- 一
胆2-2 Android 中硬件抽象扆的結杓

AndroidBSP典型的实瑰方式是杓建硬件抽象扆和 驱劫程序, 硬件抽象尼调用驱劫程


序。在迏神方式中, Android
系统的核心部分实际上美心的只是硬件抽象扆, 并不美心驱劫
程序。 迏祥做的好灶是将Androd
i 系统的部分功能和Linux中的驱劫程序隔禽,Android

依賴于Linux的驱幼程序。対于同 一 秤功能的实瑰, 可能具有不同的驱劫程序。 在某些情
況下, 硬件抽象展是 杯准的, 迏祥就只需要实瑰驱劫程序即可。 迏秤情況下的驱劫程序,
一般也是Linux中的杯准的驱劫程序。

17 '
•oo Android 板级支持与硬件相美子系统

�2.4.2 硬件抽象屏接口方式

1. hardware 模玦的方式

Android 的 libhardware 庠提供了一 秤不依賴編洋时綁定的机制, 它可以幼态加裁硬件


抽象扆, 硬件模玦方式的硬件抽象扆架杓如囹 2-3 所示。

An如沮本地框架扆
根据兩齿掙调用的模坎

『;:二:河二二二·—�l
libhardware.so 某介模坎的接口失文件

一!------••------•--•--- •--"-•---•----- ____cccccoT一


I
: 內核中的驱劫程序

匕-一一一— ccccJ
囹2-3 硬件模玦方式的硬件抽象扆架杓


在 libhardware 的接口中定又了通用的內容和各令硬件模坎的 id, 每 介硬件模玦需要
被实現成所要求的接口形式, 并生成单袖的幼态庠文件 (*.so)。
在使用硬件抽象扆的辻程中, Android 系统的框架展将调用 libhardware 的接口, 根据

每 介模玦的心将在指定路往劫态打升 (dlopen) 各介硬件模玦,然后找到符另 (dlsym),
调用硬件模玦中的各介接口。
接口的调用方式是 一 致的, 只是在不同系统中所打升的硬件模玦是不相同的。 在
Android 系统中各科硬件模玦在目才示系统中路往为 /system/lib/bw/ 。 例如, gralloc.default.so
表示默从的 gralloc 模玦(用于晁示), sensors.goldfish.so 表示默从的伟感器模玦。
libhardware 的接口在以下目汞中定又:

struct hw_module_t 結杓体用于定叉了硬件模玦的格式, 如下所示:


typedef struct hw_module_t {
uint32_t tag; // tag, 需要被初始化为HARDWARE MODULE TAG
uint16_t version_major; //主版本另
uintl6 t version minor; II次版本另
const char *id; II模玦杯沢
const char *name; II模坎的名椋
const char *author; II模玦作者
struct hw_module_methods_t* methods; II模坎方法
void* dso; II模玦的dso
uint32 t reserved[32-7]; II填充字节,为以后使用


struct hw_module_t 結杓体定叉了 介硬件模坎的信息。 在各令具体硬件模玦中, 需要
一 "
以迏令結杓体为第 介成昃, 即表示 继承"了迏令結杓体。

, 18
第2章 Android系统 BSP 部分工作 oo•


struct hw_module_methods_t 是 令表示模玦方法的結枸体, 如下所示:
typedef struct hw_module_methods一_t {
int (*open) (const struct hw_module_t* module, const char* 這 ,
struct hw device t** device); //打升没各的方法
J hw module methods t;

struct hw_module_methods_t 結杓体只包含了 介打玕模玦的函敖指針, 迏介結杓体也

作为 struct hw_module_t 結杓体的 今成贝。

struct hw_device_t 表示 介硬件没各, 如下所示:
typedef struct hw_device_t {
uint32_t tag; /// tag需要被初始化为 HARDWARE_DEVICE_TAG
uint32_t version; // hw device t的版本另
struct hw module t* module; II引用迏介没各屑于的硬件模玦
uint32_t reserved[12]; II填充保留字节
int (*close) (struct hw_device_t* device); II夫朗没各
hw device
' ''
t;

struct hw_device_t 也是需要被具体实現的結枸体包含使用的, 令硬件模坎可以包含
多令硬件没各。
硬件的具体调用流程如下所示。
(I) 通迥 id 得到硬件模玦。
(2) 从硬件模玦中得到 hw_module_ methods_t, 打玕得到硬件没各 hw_device_ t。
(3) 调用 hw_device_t 中的各令方法(不同模玦所实現的)。
(4) 调用完成, 通迥 hw_device_ t 的 close 美困没各。
模玦打升到使用的流程如囹2-4所示。

硬件模玦的调用者

hw_module_t { hw_module_methods_t { hw_device_t {


.method .open .close
} .xxx .xxx
} }

囹 2-4 硬件模玦方式的调用方式

19 '
•oo Android 板级支持与硬件相美子系统

在以上的流程中, 坯需要 libhareware 提供 一 介得到模玦的函敷, 迏介函數就是


hw_get_module(), 如下所示:
int h辶get_module(const clrnr :j.d, con!lt struct hJt_m<:Jdul免_t _**moguJsiJ.;

hw_get_modu原)函數的实瑰在 hardware/libhardware/ 目呆的 hardware.c 文件中,其內容


如下所示:
int hw get module(const char *id, const struct hw_module_t **module){
int status;
int i;
const struct hw_module_t *hmi = NULL;
char prop(PATH_MAX];
char path(PATH_MAX];
for (i= O ; i<HAL_VARIANT_KEYS_COUNT+l ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys(i], prop, NULL) == 0) { continue;)
snprintf(path, sizeof(path), 咽s/%s.%s.so", //得到模坎的名稔
HAL LIBRARY PATH, id, prop);
) else {
snprintf(path, sizeof(path), "毛s/%s.default.so", //得到默从模玦的名秘
HAL LIBRARY PATH, id);

if (access(path, R_OK)) { continue; }


break; //找到模玦, 然后退出

status = -ENOENT;
if (i < HAL VARIANT KEYS COUNT+l) {
status = load(id, path, module); II循坏打玕模玦的fifJ态庠

return status;

由 hw_get_ modu園)函啟的內容可見,执行的是 一介幼态査找的迥程,找到硬件劫态庠


(*.so) 打玕, 音没有劫态庠的时候, 将打玕默从的庠文件 (*.default.so) 。
在 hw_get_modu園)函數中调用的 load()函數, 其主要內容如下所示:
static int load(const char *id, const char *path, const struct hw_module_t **pHmi)

int status;
void *handle;
struct hw_module_t *hmi;
handle = dlopen(path, RTLD_NOW); II迸行劫态庠的打升
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
II 省略部分內容

lo呻)函敖实际上执行了 一介劫态打玕 (dlopen) 和劫态取出符另 (dlsym) 的近程。 迏


令這程解除了在編洋阶段的 Android 本地框架対特有的硬件模玦的依賴。
硬件模玦的调用方式如下所示:
xxx module t * gModule;
xxx device t * gDevice;

xxx module t const* module;

�20
第2章 Android 系统 BSP 部分工作 ooe

err = hw_get_module{XXXX_HARDWARE_MODULE_ID, {canst hw_module_t **)&module);


if {err == 0)
gModule = (xxxx_module_t*)module;
gModule->ModuleFunction {) ; //调用模決的函敷
gDevice->DeviceFunction(); //调用没各的函敖

通常情況下, 硬件模玦的调用者是 Android 中的本地框架扆, 它們调用接口定叉的杯


准, 而不是接口的实瑰。
libhardware 的接口染文件中, 除 hardware.h 之外, 其他各介染文件是相互并列的, 每

介文件表示了一 秤硬件抽象扆。
使用 hardware 模坎形式杓建的幼态庠不需要被其他部分镱接, 而是在运行的近程中通
迥 Linux 系统常用的 dlopenO和 dlsym() 函數幼态地打玕和取出符兮釆使用。

2. 调用预定乂接口的方式
调用预定又接口的方式是在失文件中定又硬件抽象毘的接口, 由实瑰者根据接口实現,
迏神方式实际上并没有完全将硬件抽象尼和 Android 的本地框架分升, 其好姓是接口的定
又和实瑰比絞简单。
hardware_ legacy 庠中提供了一 些各自狓立的接口, 由用户实瑰后形成庠, 被直接镱
接到系统中。 迏是实瑰硬件抽象扆最简单也是最直接的方式。 hardware_legacy 的染文件
路往为:
e hardware/libhardware_legacy/include/hardware_legacy
Android 中的藍牙庠 bluedroid 与之癸似, 也是采用同祥的方式, 其失文件的路往为:
e system庫 uetooth/bluedroid/include/bluedroid/bluetooth.h
hardware_legacy 庠中包含了几令 C 接口的文件, 如 power 、 wifi 、 vibrator 等, 同时在
hardware_legacy 庠中也包含了 power 、 wifi 、 vibrator。在适配 一 介新的硬件系统时, 可以根
据需要去实現迏几介庠, 也可以使用系统默从的实瑰方式。
OpenGL 和屯话部分也突似于直接调用接口的方式, 但是它亻fJ使用的是 dlopen 劫态打
升的方式, 并且可以迭掙使用不同的庠。

3. C丑的继承实瑰方式
使用 C++突的继承方式实現硬件抽象扆, 也是 Android 中的 一 神方式。 为了使用迏秭
方式,Android 平台定叉了 C丑-的接口。由具体的实現者继承实現迏些接口,同时在 Android
系统中, 通常也有通用的实現方式, 可以作为 - 今简易的实現或者"粧C stub)" 的作用。
使用 C++突的继承方式的硬件抽象扆結杓如囹 2-5 所示。
在迏神实瑰方式中, 具体的硬件抽象扆通常要求被編诵为指定名杯的幼态庠, 由本地
框架庠镱接; 通用的实瑰被編诵成靜态庠C *.a), 本地框架庠镱接送些靜态庠的时候, 其
实就是包含了它亻fJ在其中的。 在某令系统中, 究竟是使用特定硬件抽象扆坯是通用的硬件
抽象扆, 通常需要根据編诵宏米指定。

21�
• o o Android 板级支持与硬件相美子系统


本地框架庠

®劫态镱接 ®靜态镱接
xxxxHardwarelnterface

箱,'現
}

硬实
定台

,
','
,',
','
,'
(libxxx.a)


','









t

,
','
,',
','
,








-
囹2-5 使用C++奕的继承方式的硬件抽象扆結杓

Android 2.x 及之前版本的照相机和音頻系统使用的是 C++炎的继承方式。


4. 直接调用驱劫
在 Android 中有一些比絞简单的子系统,并没有在袖立存在的硬件抽象扆,也就是流,

实瑰其硬件抽象功能的部分不在单袖的代碼中。 科常見的情況是由JN1部分代碼直接调
用驱劫程序的没各节熹或者使用 sys 文件系统。 迏秤直接调用驱劫的方式 一 般适用于比絞
简单的子系统。
警扳器子系统在JNI 代碼中直接调用字符没各节熹。 屯池信息子系统在JNI中直接访
祠 sys 文件系统。 检測耳机是否插入的功能实际上也通迥在JNI中访冏 sys 文件系统完成。

2.5 各介子系统的移植方式
Android 中各令子系统的移植实現方式各不相同,主要是驱劫程序和硬件抽象尼两介方
面的內容。 在不同的 Android 升源版本中, 有些硬件抽象扆的实現方式也友生了変化。
Android 各令子系统的工作量各不相同,移植的內容和工作量也不相同。対于每 一令子系统
实珗的內容需要分別考慮。

�2.5.1 Android 2.3中的实瑰方式


Android 2.3 作为 一 令「为使用的 Android 版本,其中很多子系统的实瑰方式比絞典型。
Android 2.3 版本的各介子系统的 BSP 部分大部分继承自前面版本的 Android 系统,并
且被 Android 4.x 等后维版本继承。
Android 2.3中各介子系统的 BSP 支持方式如表2-1所示。
表2-1 Android 2.3中各介子系统的BSP支持方式
子系统 驱劫程序 硬件抽象展(或相肖的部分) 沇 明
晁示 析准的 FrameBuffer 或其他 gralloc 硬件模坎 适用于 Android 2.x

用户榆入 Event 炎型的 Input 没各 EventHub (Android 杯准) 坯包括配翌文件


屯话 非杯准 劫态升友的插件庠

22

第2章 Android 系统 BSP 部分工作 ooe

维表
子系统 驱劫程序 硬件抽象展(或相肖的部分) 沇 明

定位 非杯准(大都基于UART) gps硬件模坎 坯有AGPS部分

wpa (Lmux柝准)和 wifi庠在


元鈛局域两 柝准Wlan驱劫和甘潭
wifi庠(Android杯准) hardware_legacy中

blueZ庠(Linux杯准)和 bluedroid是Android 一 介
藍牙 柝准Bluetooth驱劫和初汶
bluedro1d庠(Android耘准) 秧立的封裝庠

音颜 Alsa,OSS或其他驱劫程序 C丑继承的硬件抽象扆 包括音颜的策略部分

视颜榆出 FrameBuffer、 v4l2或其他 overlay硬件模坎 需要实現其调用者

掇像失 多为v4l2, 也有其他驱劫 C廿继承的硬件抽象扆

OpenGL抽象昆, 使用配沉文件
3D加速 非柝准 适用于Android 2.x
迸行配置

多媒体編解碼 非杯准 Skia和Open Max的插件方式 包括囹像、 音视颜等

位炔夏制 非杯准 copyb1t硬件模玦

伟感器 非杯准 sensors硬件模玦

基于Android柝准的
振劫器 直接调用sys文件系统或其他 在hardware一legacy中
timeoutput驱劫框架或其他

背光和指示打 led驱劫或其他 lights硬件模坎

Android柝准的Alarm驱劫
帑告器 JNI直接调用坎没各 驱劫情況比絞特殊
和Linux杯准的 RTC驱幼

屯池信息 Power Supply驱劫 JNI直接调用sys文件系统

�2.5.2 Android 2.2及之前的实瑰方式


在 Android 2.2 版本及之前, 有些子系统曾經使用不同的方式实現。 隨着版本的升级,
有些实現方式已綬不再使用。
Android 2.2 及之前几介子系统的 BSP 支持方式如表 2-2 所示。
表 2-2 Android 2.2 及之前几介子系统的 BSP 支持方式
系 统 驱劫程序 硬件抽象展(或相肖的部分) 沇 明

昱示 杯准Framebuffer DisplaySurface直接调用驱劫 Android l.x版本

30加速 非柝准 OpenGL硬件庠 Android l.x版本

定位 非柝准(多为UART) 直接接口 Android 2.2及之前

以上列出的是結杓変化絞大的子系统。事实上,每令硬件相美的子系统在版本升级时,
其結杓都佘友生変化, 常見的変化是增加 一些接口。
另外几介変化絞大的子系统如下所示:
.用户榆入系统的主干呈然都是基于 Input 没各,但是新l日版本中虛姒按犍、校准的姓
理方法不同, 为了支持 NDK, Android 2.3 将很多实現移到了本地扆。
`音颜子系统早期的若干介版本并不包括单袖的策略部分。
• 伟感器子系统 Android 2.2 的版本并没有本地居的庠实現, 采取由 JNI 直接调用硬件
抽象展的形式, 并且硬件抽象扆的接口和 Android 2.3 也并不相同。

23�
• O O Android 板级支持与硬件相美子系统

�2.5.3 Android 4.x中的实瑰方式


Android 4.x 是 Android 比絞新的版本, 其中大部分子系统的实現方式和 Android 2.3 相
同, 也有一部分子系统的結杓做了改変, 主要的改劫在用户交互和多媒体部分, 并且新增
了杯准化的近汤通信子系统。
Android 4.x 中几介升级的子系统的 BSP 支持方式如表 2-3 所示。

表2-3 Android 4.x中几介升级的子系统的BSP支持方式


子系统 驱劫程序 硬件抽象展(或相肖的部分) 讠兑 明

晁示 杯准的 Framebuffer 或其他 gralloc 和 tb 硬件模坎 增加了 介模坎

用户榆入 Event 哭型的 input 没各 Event 扆 硬件抽象展是抽立的庠

音弱 Alsa 、oss 或其他驱劫程序 Aud10 等硬件模坎 使用若干介劫态庠实現

视颜榆出 Framebuffer 、 v4l2 或其他 hwcomposer 硬件模坎

撮像失 多为 v4l2, 也有其他驱劫 camera 硬件模坎

近 :f:汤通信、屯源管理、本地时冏和密钳等几令小型的硬件模玦在 Android 4.0 至 Android


4.2 版本被增加作为新的硬件抽象屄。

2.6 与硬件抽象扆相美的框架扆 H呆
实际上, 各介硬件抽象尼的內容在不同的 Android 版本中変化并不大, 但是 Android
框架扆的代碼則有 一 些位置上的変化。 从友展的迥程上來看, 从 Android 2.3到Android 4.1
版本之阅的変化都不大, 而 Android 4.2 則有一 些比絞大的変化。

�2.6.1 一直保持不変的代碼
Android 框架扆的主要內容在 frameworks/base 中, 其中 一 些重燕目呆自 Android 2.3到
Android 4.2 版本基本不変, 甚至从 Android 1.x 版本升始都没有岌生大的変化。
• include/: 框架扆本地庠的失文件 (Android 4.x 将部分內容移出)。
libs/: 框架扆本地庠的源代碼 (Android 4.x 将部分內容移出)。
• cmd/: 框架尼的本地可执行程序和 Java 命令行程序源代碼。
• core/java/: 框架庠 (framework.jar) 的 Java 代碼。
• core/jni/: 框架庠的 JNI 的代碼。
• services/java/: 服各庠 (service.jar) 的 Java 代碼。
• services/jni/: 服各庠的 JNI 的代碼。
• media/java/: 多媒体部分的 Java 代碼。
• media/jni/: 多媒体部分的 JNI 代碼。

�2.6.2 框架居的本地代碣
在 Android 4.2 中, frameworks/native 是 一令与 frameworks/base 并列的目呆, navtive

, 24
第2章 Android系统BSP部分工作 ooe

的含叉为本地扆的內容, 原本在base目汞中的 一 些本地的內容被拆分到native目汞中。


几介主要的目汞如下所示(內部的結杓没有変化)。
e include/: utils 、 binder 、 ui和gui等几介本地庠的染文件。
e libs/: utils 、 binder、 ui和gu i等几介本地庠的实珗。
e services/: sensorservice和surfaceflinger等几介服各庠的代碼。
e cmds/: sensorservice和surfaceflinger等几令可执行程序。

�2.6.3 音頻视頻相夫的代碣

在Android 4.2中, frameworks/av是 令与frameworks/base并列的目呆, av的含乂就

是音颜audio和 视颜vidoe, 原本在 base 目汞中的 些与音颜视颜相芙的內容被拆分到av

目呆中, 并且原本的下 级目呆結杓保持不変。
几介主要的目杲如下所示。
e include/camera/: camera庠的失文件。
e include/media/: media庠(包括media和audio部分) 的失文件。
e camera/: camera庠的源代碼。
e media/libmedia: media庠源代碼。
e media/libmediaplayerservice/: libmediaplayerservice庠的源代碼。
e media/libstagefright/: libstagefright庠的源代碼。
e media/mediaserver/: mediaserver可执行程序的源代碼。
e services/audioflinger/: audioflinger服各庠的源代碼。
e services/camera/: cameraservice庠的源代碼。
e cmds/stagefright/: stagefright可执行程序。
以上目汞 一 般只是上尼的位置笈生了変化, 目汞內部的結枸則没有変化。

25�
第3章
Android的Linux內核和驱劫

3.1 Android的Linux內核概述

�3.1.1 几令內核工程
Android 系统的 Linux 內核与硬件平台相美, 每令硬件平台具有各自狓立的代碼合庠。
每令代碼合庠的 Linux 內核主要不同的地方是板级移植和驱幼程序, 并且佘髄着 Linux 內
核的版本升级,各令平台的內核从 Linux 2.6.x 到 Linux 3.x 都有,除了遵循 Linux 內核中的
不同版本的架枸, 它亻(]的差別并不大, 如表 3-1 所示。

表 3-1 不同硬件平台的Android 內核
內核工程名都 対成的硬件 描 述

kernel/common Goldfish虛姒灶理器(作为通用的
Common Android Kernel Tree
kernel/goldfish Android的Linux內核使用)

kernel/msm 高通的MSM和QSD系统灶理器 Kernel tree for MSM7XXX family and Android on MSM7XXX

kernel/omap 德州仅器的OMAP系列灶理器

kemel/samsung 三星的系列灶理器 Kernel tree for Samsung system�on Android

kernel/tegra NVIDIA的Tegra 系列灶理器 Kernel tree for NVIDIATegra family SO!Cs on Android.

� 提示: 元讠令上屋的代碼的情況如何, 各今硬件平台的 Linux 代碼均为升源。

�3.1.2 內核工程的维讠圣工具鎚

Android 內核需要使用特定的交叉編洋工具迸行編诵,編洋內核的工具镱不 定和編洋
Android 系统的工具镱相同。 Android 源代碼中工具镱的路往为: prebuilt/linux-x86/toolchain/,
其中包括几介子目汞。
a arm-eabi-4.3. l 、 arm-eabi-4.4.0 、 arm-eabi-4.4.3 等: ARM 体系的交叉工具镱。
a i686-linux-glibc2.7-4.4.3 和 i686-unknown-linux-gnu-4.2. l: x86 的工具镱。
第3章 Android 的 Linux 內核和驱劫 ooe

e sh-4.3.3: SH 灶理器交叉的工具镱。

�3.1.3 用户空阅夫注的內容
各令硬件平台的 Linux 內核部分有差昇,巨別主要体瑰在硬件的驱劫和相失的信息中。
在运行中,没各节意的目汞是/dev/目泵和其子目汞中的內容,除了杯准 Linux 和 Android
的通用驱幼, 其他的內容 一 般为具体硬件的没各节煮, 例如, /dev/grap扣cs/fbO 表示晁示驱
幼 (Android 的特殊没各节燕路往), /dev/input/目豪中为各令諭入没各的驱劫。
sys 文件系统中的硬件相栄部分的內容也是 BSP 部分的重要的信息。
e /sys/devices/platform/: 対皮的各介內容是各令平台没各 (platform_device), 通常在
板级別內容中定又, /sys/bus/platform/devices 目呆中的各介內容是到它亻I']的连接。
e /sys/bus/platform/drivers/: 対皮的各介內容是各介平台驱劫 (platform_driver), 通常
在各令驱劫程序的实現中定又。
Android 系统的姓理器大多为 soc, 其中包括了若干令惡鈛,在 sys 文件系统中也具有
相美的信息。 例如, 対于 12C 惡綫相美的內容具有以下目呆。
圖 /sys/bus/i2c/devices/: 表示 l2C 惡鈛上的没各。
e /sys/bus/platform/drivers/: 表示 l2C 惡鈛上的相美驱劫。
e /sys/devices/i2c-<>/: 表示 I2C 的某介适配器中的没各。

3 .2 Android� 用驱幼和組件

Android 考用驱劫和組件并非 Linux 中杯准的內容, 而是純軟件的內容, 与体系結杓和


硬件乎台元美。 由于 Android 考用驱劫是純軟件的內容, 因此在平台移植的迥程中基本不
需要做更改, 最多是迸行配置、 迭拌考用驱劫和組件是否使用。

�3.2.1 甩源管理部分
wakelock (醒的狀态钅典) 机制与 earlysuspend (早期挂起)結合, 提供了 Android 袖有
的屯源管理机制。 相比 Android 系统其他的內核驱劫和組件, 內存管理是全局的功能, 対
具体驱劫程序的实現部分有所影咆。
它亻I']的失文件是 include/linux/目呆中的 wakelock.h 和 earlysuspend.h, 实現的內容在
kernel/power/目設包括 wakelock.c、 userwakelock.c、 earlysuspend. c 等几介源文件。
wakelock 可以阻止系统迸入挂起, 也就是一 直醒的狀态, 而 earlysuspend 是一介全局
的功能, 用于辻各令模玦注冊早期挂起的操作。
例如, 在 Linux 系统的命令行中迸行如下操作:

向 state 写入"mem" 等命令本身是 Linux 屯源管理的杯准操作, 含叉为挂起到內存, 在


普通的 Linux 系统中, 将直接迫使內核迸入挂起 (suspend) 狀态。 但是在 Android 中, 将
调用各介內核模玦 early_suspend 中的 suspend 函敷指針, 调用完成后, 系统迸入"早期挂

27�
•oo Android 板级支持与硬件相美子系统


起 狀态。 此后, 如果系统没有任何 wakelock 钅典, 将继维调用各令模玦的挂起功能, 迸入

真正的挂起狀态; 如果有 wakelock 钅典, 系统将保持"早期挂起 狀态, 此时的 CPU 依然
是可以运行的。

�3.2.2 staging中的組件和驱劫程序
Linux 內核的 drivers/staging/android/目呆中放置了 Android 系统阶段性的內核組件及驱
幼程序。 迏介目汞中所有的內容都是 Android 的 Linux 內核特有的。
目汞中包含了 Kconfig 和 Makefile 文件, Makefile 的內容如下所示:
obf-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
obj一$(CONFIG ANDROID LOGGER) += logger.o
obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o
obj一$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
obj-$(CONFIG_ANDROID_TIMED_GPIO)+= timed_gpio.o
obj-$JCONFIG_ANDROID_LO�_MEMORY_KILLER) += lowmemorykiller.o

其中, binder 和 logger 是两令普通的 misc 驱劫程序, timed_output 是 Android 特有的


驱劫程序框架; timed_gpio 是基于 timed_output 的一介驱劫程序; lowmemorykiller 是 一令
內存管理的組件; ram_console 是 一 介利用控制台驱劫的框架。
a binder .c 和 logger.c: 基于 MISC 的字符没各驱劫程序。

a lowmemorykiller.c 和 logger.c: 令內存管理的組件。
a timed_output.c: 一令没各驱劫程序的框架, 使用 sys 文件系统。
a timed_gpio.c: 基于 timed_output 框架的一 秤驱劫程序。
a ram_console.c: 用于调试的內核控制台, 使用 proc 文件系统。

1. Binder 驱劫程序

Binder 驱劫程序为用户扆程序提供了 IPC (迸程 l 司通信)支持, Android 整令系统的运
行依賴 Binder 驱幼。 Binder 提供給用户空冏的接口是主没各弓为 10 的 Misc 字符没各, 次
没各另是劫态生成的。 在用户空阅中, Binder 没各节燕为 /dev/binder。
Binder 驱劫程序內容由 binder.h 和 binder.c 迏两今文件組成。 Binder 没各対用户空冏主
要提供 mmap 、 poll 、 ioctl 等接口。
binder.h 中的 BinderDriverRetumProtocol 和 BinderDriverCommandProtocol 两令枚举奕
型分別表示返回和命令, 在 Binder 驱劫的 ioctl 中使用, 內容如下所示:
enurn BinderDriverReturnProtocol ( II 表示返回的整敖值
BR ERROR = _IOR('r', 0, int),
BR OK = IO ('r', 1),
II其他返回值

enurn BinderDriverCornrnandProtocol { II表示命令的整敷值
BC_TRANSACTION = _row ('c', 0, struct binder_transaction_data),
BC REPLY = IOW('c', 1, struct binder''transaction data),
II其他命令

Binder 驱劫的实瑰和使用都比絞夏朵。 在用户空冏內需要対其调用 poll 接口阻塞和

�28
第3章 Android 的 Linux 內核和驱劫 ooe

mmap 映射其中內容。
在 Android 系统的用户空阅中,servicemanager 可执行程序和 Binder 庠都调用了 Binder
驱劫程序, 然后再提供机制給 Android 系统的其他框架部分使用。

2. Logger 驱劫程序

Android 的 Logger 驱劫程序为用户扆程序提供日志支持。 Logger 提供給用户空冏的接


口是主没各另为 10 的 Misc 字符没各, 次没各另是劫态生成的 (3 介)。
在 Android 系统的用户空伺中, Logger 在 /dev/log/ 目呆中有 3 介没各节燕, 如下所示。
e /dev/log/main: 主要的 log 的没各节燕。
e /dev/log/event: 事件的 log 的没各节煮。
e /dev/log/radio: Modem 部分的 log 的没各节煮。
Logger 驱劫程序为用户空冏提供了 ioctl、 read 和昇步 write 等接口。
Logger 驱幼程序中几令 ioctl 的命令在 logger.h 中定又如下所示:
#define LOGGERIO OxAE
#define LOGGER GET LOG BUF SIZE IO( LOGGERIO, 1) // log的大小
itdefine LOGGER GET LOG LEN IO( LOGGERIO, 2 ) // log可用的板度
itdefine LOGGER GET NEXT' ENTRY LEN '
IO( LOGGERIO, 3) II下 一 介入口的伕度
itdefine LOGGER FLUSH LOG IO( LOGGERIO, 4 ) II刷新log

Logger 驱幼程序中并列地实現了几介没各, 它亻i'J 都具有渡接口和昇步写 (aid_write)


接口。 対于非本用户且非本組的用户, Logger 驱劫程序的没各节燕是只可写不可涼的。 也
就是祝, 任何程序都可以榆出信息, 但是特定用户才可以荻取信息。
在 Android 的用户空冏, liblog 庠和 logcat 可执行程序是対 logger 没各迸行的调用,通
迥后者看到的日志信息米自 logger 没各。

3. Lowmemorykiller 組件

Lowmemorykiller 提供了在低內存狀況下糸死迸程的功能。 使用 lowmemory如lier 可以


在用户空伺没置 一介內存的國值, 通迥迏介閾值釆判斷迸程是不是将要被糸死。
Lowmemorykiller 的实現只包括 lowmemorykiller.c 一令源文件。 本組件通迥调用 Linux
內存管理系统的接口, 注朋 一 介 shrinker, 迏令 shrinker 就是 Lowmemorykiller 的实瑰。 主
要內容如下所示:
static struct shrinker lowmem_shrinker = {
.shrink = lowmem_shrink,
.seeks = DEFAULT SEEKS * 16
};
module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO I S_IWUSR);
module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO I S_IWUSR);
module_param_array_named(minfree, lowmem_minfree, uint,
&lowmem_minfree_size, S一IRUGO I S_IWUSR);
module_p 亨ram_named(debug_lev�L,_ lo_!'lmem_�ug一1 斛咭土_uint, S一巷迷验 ,_s_卫ms�;_
Lowmemorykilier 使用了以下 4 介 sys 系统文件作为到用户空冏的接口,几介文件的含
又如下所示。
e /sys/rnodule/lowmemorykiller/parameters/minfree: 最小內存的洞值, 整敖敖組。

29�
•oo Android 板级支持与硬件相美子系统

a /sys/module/lowmemorykiller/parameters/adj: 几今级別, 整數數組。


• /sys/module/lowmemory如Her/parameters/debug_ level: 调试级別, 整數。
oom_adj 是內存管理机制的 一 秤策略, oom_adj (Out of Memory Adjust) 的數值越小,
代表迸程的优先级別越高。 例如, 岂 minfree 小于敖值 A 时, 結束 oom_adj 大于敷值 B 的
迸程。
Android 用户空伺的成用程序被 system_server 的 ActivityManagerService 奐靑, 前台皮
用迸程的 oom_adj 为 O, 后台为 15, 本地的守护迸程为免值。

4. Timed Output 驱劫程序框架


Timed Output (定时榆出)实际上是一 介驱劫程序的框架, 用于定时友出 一 令諭出。
Timed Output 框架为注冊的每 一 介驱劫提供 sys 文件系统的接口。
Timed Output 驱劫程序框架如囹 3-1 所示。

read/ write
用户空冏
/sys/cJassjti1t1ed_output/ {没各}/enable
內核空伺
show I store ! 建立{没各目汞}

;:江
!;"" ,., ..一-一,••一-一·
具体Timed Outpu頃璃
(实瑰timed_output_dev)

des�re,isre,

Timed Output驱劫程序框架
(timed_output.c)

招3-1 Timed Output 驱劫程序框架

Timed Output 将注冊/sys/class/timed_output/目呆,每 一 介注冊实珗的 Timed Output 没各


将在其中建立 一介与没各同名的子目呆, 子目汞中具有 一 介 enable 文件, 対迏今文件的渡/
写用于晁示和控制没各。
timed_output.h 中定又 timed_output_dev 結杓体, 其內容如下所示:
struct timed_output_dev {
canst char *name;
void (*enable) (struct timed_output_dev *sdev, int timeout); //使能没各并没置
int (*get一戶 me) (struct 戶 med_output_dev *sdev) ;. //返回剩余时冏(mil 訌 seconds)
struct device *dev; //以下为私有數据
int index;
int state;

Timed Output 提供了 一令驱劫程序的框架結杓, 需要各介没各实現 enable 和 get_time


迏两令接口, 框架通迥 sys 文件系统提供用户空囘的接口。 Timed Output 没各的注朋和注銷
函數如下所示:

�30
第3章 Android 的 Linux 內核和驱劫 ooe
extern int訌med_output_dev_register(struct timed_output_dev *dev);
extern void timed_ou_tput_dev_unregist_er(struct timed_output_dev *dev);

在 timed_output.c 的实現中, 直接调用 sys 文件系统, 主要的內容是 show 和 store 迏两


令函數, 如下所示:
static ssize t enable show(struct device *dev, struct device attribute *attr,
char *buf) {
struct timed_output_dev *tdev = dev_get_drvdata(dev);
int remaining = tdev->get_time(tdev);
return sprintf(buf, "%d\n", remaining);

static ssize t enable store(struct device *dev, struct device attribute *attr,
const char *buf, size_t size) {
struct timed output dev *tdev = dev get drvdata(dev);
int value;
sscanf(buf, "%d", &value); //更新內容
tdev->enable(tdev, value); //使能没各
return size;

static DEVICE_ATTR(enable, S_IRUGO I S_IWUSR, enable_show, enable_store);

enable 文件是每介 Timed Output 没各中都具有的文件, 写迏令文件表示没置定时器时


阅并启劫定时器, 涙迏介文件表示査看定时器剩余的时冏。
基于 Timed Output 驱幼程序框架实現的驱幼程序, 則主要需要实現 enable 和 get_time
迏两介函敖指針, 需要驱劫程序自己去实瑰。

5. Timed GPIO 驱劫程序

Timed GPIO 实际上是一 介基于 Timed Output 驱劫程序框架的驱劫程序,用于定时控制


GPIO (General Purpose Input Output, 通用諭入/榆出端口)。 它调用 Timed Output 框架注冊
了 一 今驱劫程序, 在 enable 和 get_time 函數中调用 gpio 子系统的內容实瑰功能。
Timed GPIO 驱幼的名稔在 timed_gpio.h 中定又, 如下所示:
ildefine TIMED_GPIO_NAME "timed-g户o"

根据 Timed Output 的驱劫架杓 /sys/class/timed_outputltimed-gpio 目汞中的 enable 文件是


Timed GPIO 対用户空阅的接口。
在驱劫的实現迥程中, struct timed_gpio 作为迏令驱劫程序的私有結杓体, 保存 GPIO
的相美敷据。 在 timed_gpio.c 中, 调用 Timed Output 的接口, 注朋了迏令驱劫程序。

6. Ram 控制台
一 一
Ram Console 提供了 秤可以鋪助调试的內核机制, 其实現方式是利用 坎內存杓建

介虛姒的 Console 没各。 音內核中打印信息(调用 printk) 时, 调试信息将同时榆出到迏
令没各中。 Ram Console 提供給用户空岡的接口是 Proc 文件系统中的 proc/last_kmsg 文件。

ram_console.c 的初始化代碼在 Proc 文件系统中增加了 介 last_kmsg 文件, 其文件的
操作在 ram_console_file_ops 中实現。 last_kmsg 文件支持涙取, 淩取的內容就是 Console 的
諭出信息。
Ram Console 通迥/proc/last_kmsg 的文件給用户空冏提供信息, 其內容表示 Linux 內核

31�
•oo Android板级支持与硬件相美子系统

最后打出的信息,每糸信息前面的[]中的內容表示信息的时冏 。

�3.2.3 几介主要核心模玦

1. Ashmem模玦
Ashmem (Anonymous Shared Memory)的含又 是匿名共享內存,可以为用户空l可程序
提供分配 內存的机制。Ashmem提供給用户空岡的接口 是主没各另为10的Misc字符没各,
次没各弓是劫态生成的。在用户空冏中,Ashmem的没各节燕为/dev/ashmem 。
Ashmem驱劫程序的配置迭項为CONFIG_ASHMEM, 対皮于 init/目汞中的Kconfig 。
由于 其具有內存管理的性原,Ashmem的源代碼是內存管理的mm 目呆中的ashmem.c, 在
include/linux/中的ashmem.h文件是Ashmem系统的失文件。
ashmem.c 中提供了內存分配的机制,也实現了一介 Misc字符没各作为到用户空阅的
接口,支持mmap和ioctl 操作 。 ashmem_area为 其核心結杓体,range_alloc 、 range_del和
range_shrink几介函數完成 內存的操作 。
Ashmem的ioctl的命令在 ashmem.h中定又, 內容如下所示:
#define ASHMEM SET NAME IOW( ASHMEMIOC, 1, char[ASHMEM NAME LEN])
#define ASHMEM GET NAME IOR( ASHMEMIOC, 2, char[ASHMEM NAME LEN])
#define ASHMEM SET SIZE IOW( ASHMEMIOC , 3, size t )
#define ASHMEM GET SIZE IO( ASHMEMIOC, 4 )
#define ASHMEM SET PROT MASK IOW( ASHMEMIOC, 5, unsigned long)
#define ASHMEM GET PROT MASK IO( ASHMEMIOC, 6 )
#define ASHMEM_PIN IOW( ASHMEMIOC, 7, struct ashrnem pin)
#define ASHMEM_UNPIN _IOW(_ASHMEMIOC, 8, struct ashrnem_pin)
#define ASHMEM GET PIN STATUS IO( ASHMEMIOC, 9 )
lldefine ASHMEM PURGE ALL CACHES IO( ASHMEMIOC, 10)

Ashmem 为 Android 系统提供了內存分配 功能, 实瑰奕似 malloc 的功能,更癸似于


POSIX的共享內存 。
Android 用户空冏 C工具庠libcutils 対Ashmem迸行封裝并提供 接口,対其他部分提供
了匿名共享內存在用户空向的调用封裝 。 Dalvik虛姒机在刨建虛姒机 时使用Ashmem作为
堆內存, 內部的內存分配均來自迏坎內存,方便了垃圾回收。

2. Pmem模玦

Pmem 主要用于 共享大量的连维物理 內存,迏朴內容常常 用于系统中額外的DSP灶理


器等硬件。 Pmem 提供給用户空冏的接口 是主没各另为10的Misc字符没各,次没各弓是
劫态生成的。
Pmem模玦的配置迭項为CONFIG_ANDROID_PMEM, Pmem的失文件是include/linux
目汞中的android_pmem.h文件,drivers/misc/目汞中的pmem.c 是其 实瑰。
在某介实际没各的实瑰中,通辻引用 android_pmem.h并 调用其中的內容,米杓建
Pmem, 如下所示:
struct pmem_region {
unsigned long offset; //巨域的偏移量
unsigned long len; //巨域的板度

�32
第3章 Android 的 Linux 內核和驱劫 ooe

);
int is_pmem_file(struct file *file);
int get_pmem_file(int fd, unsigned long *start, unsigne9 long *vstart,
unsigned long *end, struct file **filp);
int get_pmem_user_addr(struct file *file, unsigned long *start,
unsigned long *end);
void put_pmem_file(struct file* file);
VO這flush_pmem_file(struct file *file, unsigned long start, unsigned long len);
int pmem_setup(struct android_pmem_platform data *pdata,
-:-
long (*ioctl)(struct file *, unsigned int, unsigned long),
int (*release)(struct inode *, struct file *));
int pmem_remap(struct pmem_region *region, struct file *file,
unsigned operation);

相美的操作包括建立、 得到和修改 Pmem 文件, 得到用户空阅的地址。 在具体硬件实


現的迥程中, 调用以上的內容, 対 Pmem 文件迸行操作。

Pmem 提供了 mmap 和 ioctl 作为到用户空冏的接口, 些 ioctl 命令如下所示:
#define PMEM_IOCTL_MAGIC 'p'
#define PMEM_GET_PHYS _row(PMEM_IOCTL_MAGIC, 1, unsigned int)
#define PMEM MAP IOW(PMEM IOCTL MAGIC, 2, unsigned int)
#define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int)
#define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int)
#define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC., 5, unsigned int)
#define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int)
#define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC _, 7, unsigned int)

迏些 ioctl 命令包含了荻得物理內存、 映射和解除映射內存、 荻得內存尺寸、 分配、 连


接和荻得全部大小等功能。 其中的连接操作, 可以辻两介以上的迸程打玕同一 介 Pmem,
从而荻得共享內存的效果。

3. Alarm 驱劫程序
Alarm 驱劫程序为用户空阅提供了 一 令时钅中的接口。 它和 RTC 系统密切相美, 起到封
裝的作用, 同时使用了 Android 系统的 wake_lock 等功能。 Alarm 提供給用户空冏的接口是
主没各弓为 10 的 Misc 字符没各, 次没各弓是幼态生成的。 在用户空阅中, Alarm 没各节
煮为/dev/alarm。

Alarm 驱劫程序的配置逃璜为 CONFIG_RTC_INTF_ALARM C 絞新版本坯有另 介迭
項 CONFIG_RTC_INTF_ALARM_DEV), 其失文件是 include/linux/ 中的 android_alarm.h 文
件, drivers/rte/ 目泵中 alarm.c 和 alarm-dev.c (在絞新版本中才有)是其实瑰部分。'
Alarm 驱劫程序利用內核中的 RTC 部分, 但是和具体的 RTC 驱劫程序没有直接美系。
Alarm 驱劫程序本身依然是硬件元美的。
Alarm 可以提供 一些 ioctl 的命令供用户空向调用, 如下所示:
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 I ((type) << 4))
#define ANDROID ALARM WAIT IO('a', 1)
非define ALARM_IOW(c, type, size) 一IOW('a', (c) I ((type) << 4), size)
#define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec)
椿define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec)
#define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec)
itdefine ANDROID ALARM SET RTC row(•a' s , struct timespec)
itdefine ANDROID_ALARM_BASE_CMD(cmd) (cmd & -(_IO_f(0, 0, OxfO, 0)))

33�
•oo Android 板级支持与硬件相美子系统

#define ANDROID_ALARM_IOCTL_TO_TYPE(crnd) (_IOC_NR(crnd) >> 4)

以上 ioctl 命令主要用于没置警扳器的时冏, 没置 RTC (实时时钅中)的时岡, 荻取岂前


时伺等。

4. ADB Garget 驱劫程序



ADB Garget 驱劫程序是 秤 USB Garget 驱劫程序。 如果造定此 Garget 驱劫, Android

没各作为 介 USB 没各时, 提供 ADB 的接口。
在 Linux 中, USB Garget 的功能是在没各端使用的功能, 每 一 今硬件只能造定一 介。
迏介 ADB Garget 是其中的 一 介, 它实际包含了 ADB 调试功能和大容量存儲器 (Mass
Storage) 的功能。
ADB Garget 驱幼程序是在 drivers/usb/gadget 目汞中,其 Makefile 的相哭內容如下所示:
obj一$(CONFIG_USB_ANDROID) + = g_android.o
g_android-ob婪 下android.o f_adb.J? f_m箜辶結_ora9e.o

其中, android.c 为实瑰 USB Garget 功能主要的文件, f_adb.c 是 ADB 功能的文件,


f_mass_storage.c 是杯准的文件, 需要包含它的目的是为了同时实瑰大容量存儲器的功能。
实現的主要結杓体为 usb_composite_driver, 迏表示的就是 一 秤 USB Garget 功能組合的
結枸, 內容如下所示:
static struct usb composite driver android usb driver = {
.name = "android_usb",
.dev = &device desc,
.strings = dev_strings,
.bind = android bind,
};

android.c 中同时注朋了一 介 MISC 没各: /dev/android_adb_enable, 岂打玕迏令没各时,


表示使能 ADB Garget 的功能。
迏里实瑰的具体內容是根据 Android 的 ADB 的仂汶米完成的。 具体的实現在 f_adb.c
中完成, 迏介文件实現了 一 介 USB 的功能, 调用如下函數增加功能:
ret = usb_翌<:i<:i_fun_tl_ion('c_ t. &dev->function);

f_adb.c 中也注冊了一 令 MISC 没各: /dev/android_adb, 迏介没各可以�/写。


在 Android 系统的用户空阅中, /system/core/adb 目呆中的內容和 ADB 相哭。迏里生成
了主机使用的 ADB 工具和目际机器使用的 adbd 守护迸程的可执行程序。

5. Android Paranoid 國絡

Android Paranoid 冏絡是 一 令対 Linux 內核冏絡部分的改劫, 通迥送介改劫增加了冏絡


的从证机制。 迏令特性通迥宏ANDROID_PARANOID_NETWORK迸行使能。
迏介特性主要影喃了 Linux 源代碼中以下 一 些文件。
• net/ipv4/af_inet.c: IPV4 的仂汶文件。
• net/ipv6/af_inet6.c: IPV6 的仂汶文件。
• net/bluetooth/af bluetooth.c: 藍牙的t1J-汶文件。

�34
第3章 Android的Linux內核和驱劫 ooe

e security/commoncap.c: 安全性的文件。
事实上, af_inet.c、 af_inet6.c和af_bluetooth.c是3秤不同的两絡仂汶中灶理扔讠文方面
的文件, 它們在還緝上是并列的美系。
Android的染文件include/linux/android_aid.h中定乂了 美于阿絡的部分AID, 迏令內容
和杯准的Linux有所巨別, 如下所示:
hfndef LINUX ANDROID AID H
lfdefine LINUX ANDROID AID H
lfdefine AID NET BT ADMIN 3001
lfdefine AID NET BT 3002
lfdefine AID INET 3003
lfdefine AID NET RAW 3004
lfdefine AID NET ADMIN 3005
扣endif

在net/ipv4/af_inet.c中, 相美的內容如下所示:
#ifdef CONFIG ANDROID PARANOID NETWORK
非include <linux/android_a這.h>
static inline int current_has_network(void) {
return in_egroup_p(AID_INET) 11 capable {CAP_NET_RAW);

非else
static inline int current_has_network(void) ( return l;)
i/endif

在迏今地方, 迸 步检査了AID Android的用户ID),
C 如果符合才返回1, 如果没有
附加迏介特性 , 則直接返回1。
在安全性的文件security/commoncap.c中, 相美的內容如下所示:
int cap capable(struct task_struct *tsk, const struct erect *erect, int cap,
int auctit) {
#ifctef CONFIG ANDROID PARANOID NETWORK
if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; //跳這的操作
if (cap= CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0;
#enctif
return cap_raisect(crect->cap_effective, cap) ? 0 : -EPERM;

迏里的工作是检査能力方面, 增加了対AID的判斷。 如果AID符合, 則直接返回 o,


不再使用cap_raised()函敖迸行灶理。

�3.2.4 埔助的模玦和改劫

1. keychord棓U夬

keychord模玦是 介鋪助諭入功能驱劫程序, 苗用户自定叉的組合按鍵友生事件后,
可以提供通知。 keychord提供給用户空阅的接口是主没各另 为10的Misc字符没各, 次没
各另是劫态生成的。 在用户空冏中, 其没各节魚为/dev/keychord。
keychord的配置造項为CONFIG_INPUT_KEYCHORD, � 文件为include/linux/目泵中
的keychord.h, 其实現的內容为drivers/input/misc/目呆中的keychord.c。

35�
•oo Android 板级支持与硬件相美子系统

keychord.b 文件中具有如下形式的定又:
struct input_keychord {
' '
u16 version;
u16 id;
u16 count; //按鍵碼的敷目
u16 keycodes[J; II按犍碼的啟組
);

keychord 的实瑰是対普通 input 驱劫程序的一 扆封裝。 input_keychord 結杓中包含了按


鍵碼的敖組, 只有者所有的按鍵都被按下时, 才合严生通知事件。
keychord 提供給用户空阅的主要是 read 和 poll 接口, 前者用于荻得事件, 后者用于阻
塞, 坯有 write 接口用于配置迏介驱劫程序。

2. keyreset 模玦

keyreset 模玦是 介鋪助夏位功能的驱劫程序。 keyreset 提供給用户空冏的接口是主没
各另为 10 的 Misc 字符没各, 次没各弓是幼态生成的。 在用户空冏中, 其没各节煮为
/dev/keyreset。
keyreset 的配置迭項 CONFIG_INPUT_KEYRESET, � 文件为 include/linux/ 目汞中的
keyreset.h, 其实瑰的內容为 drivers/input/ 目呆中的 keyreset.c。
keyreset.h 文件具有以下的定又:
struct keyreset_platform_data (
int *keys_up; //抬起事件
int keys_down[J; //按下事件, 使用 0表示結束
);

keyreset 基于 input 驱 劫架杓米实 瑰 。 在不同乎台的 实 現近程中, 只需要使用


keyreset_platform_data 敖据結杓定又夏位按鍵, 苗几介按鍵按下、 几令按犍抬起时表示夏
位事件的友生。

3. kernel_debugger 模玦
kernel_debugger 模玦提供鋪助的內核调试信息功能。 kernel_debugger 利用榆出的方式
提供給用户空冏內核的调试信息。
kernel_debugger 驱劫程序的配置迭項是 CONFIG_KERNEL_DEBUGGER_CORE, � 文
件为 include/linux/ 目汞的 kernel_debugger.h, 实現为 drivers/misc/ 目呆中的 kernel_debugger.c。
失文件 kernel_debugger.h 具有如下定叉:
struct kdbg_ctxt (
int (*printf) (vo這*cookie, const char *fmt, ...) ;
VO這*cookie;
};
int kerne辶迤吣g_ger (萃ruct k翌g_ctxt *ctxt, char *cmd);

圭在各介驱劫程序中调用迏令 kernel_debugg叫)函數时, 荻得相皮的信息。

4. uid_stat 莫玦 t
uid_stat 模玦是一令根据用户 ID 统计其冏絡接收和友送內容的工具。 uid_stat 提供給用

�36
第3章 Android 的 Linux 內核和驱劫 oo•

户空岡的接口是 proc 文件系统的/proc/uid_stat 目汞中的各今文件。


uid_stat 的配置迭瑣为 CONFIG_UID_STAT, �文件为 include/linux/目汞中的 uid_stat.h,
其实現的內容为 drivers/misc/目泵中的 uid_stat.c。
uid_stat.h 具有如下定叉:
extern int update_tcp_snd(uid_t uid, int size);
extern int update_tcp_rcv (ui�t uid, int size);

uid_stat 将建立 proc 文件系统的/proc/uid_stat 目汞, 每今 uid 在其中具有 令子目汞,
其中又具有 tcp_snd 和 tcp_rcv 两介文件, 分別表示垓 uid 友送和接收 TCP 的情況。
如果 uid_stat 功能使能, 內核的两絡核心部分的套接字实瑰 net/socket.c 中, 将调用
uid_stat 的接口统计阿絡收友情況。

3.3 goldfish平台的內核和驱幼

�3.3.1 goldfish平台和內核概述
goldfish 是一 秤虛姒的 ARM 灶理器, 在 Android 的仿真坏境中使用。 在 Linux 的內核
``
中, goldfish 作为 ARM 体系結杓的 一 秤 机器,,。 goldfish 的內核基于杯准的 Linux 內容,
其中增加了 Android 的考用驱劫和組件, goldfish 平台板级的移植內容, goldfish 中各秤模
姒硬件没各的驱劫程序。
使用 git 下裁 goldfish 內核的方法如下所示:
$ git clone https: //android ._googlesourc免.com/kernel/�goldfish

目前使用的工程名稔为 kernel/goldfish, 另外坯有 一 介名稔为 kernel/common 的工程,


表示通用內核, 岂中的內容有 一部分也是癸似的。
迸入目汞后, 使用 一 令稔定版本的方法如下所示:
$ cd goldfish
$ git branch -r
origin/HEAD -> origin/master
origin/android-goldfish-2.6.29
origin/android-goldfish-3.4
origin/linux-goldfish-3.0-wip
origin/master
$ git checkout origin/android-goldfish-2.6.29

goldfish 平台有两介稔定的版本, 分別基于玕源的 Linux 2.6.29 和 Linux 3.4, 可以使用


不同的分支迸行迭拌。 Android 平台后维版本的友展实际上和內核版本的美系不大,因此各
今版本的內容可以通用。
在 goldfish 的 Linux 源代碼的根目汞中, 配置和編洋 goldfish 內核的迥程如下所示:
$ make ARCH=arm goldfish_defconfig .config
$ make ARCH=arm CROSS_COMPILE='::path>/arm-eabi-

没置和执行迥程中, 使用 goldfish_defconfig 作为配置文件, 在 CROSS_COMPILE=中

37�
•oo Android 板级支持与硬件相美子系统

指定交叉編诵工具的路往。
goldfish 灶理器的編涌結果, 最后的內容如下所示:
LD vm訌nux
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
OBJCOPY arch/arm/boot/zimage
Kernel: arch/arm/boot/zimage is ready

goldfish 与 ARM 乎台的其他 Linux 的編诵結果癸似,但是没有內核模玦 (*.ko) 。 vmlinux


是 Linux 迸行編诵和连接之后生成的 Elf 格式的文件, Image 是未經迥圧縮的二迸制文件,
piggy 是 一 令解圧縮程序, zlmage 是解圧縮程序和圧縮內核的組合。
在 Android 源代碼的根目呆中 vmlinux 和 zlmage 分別対皮 Android 代碼 prebuilt 中的预
編诵的 arm 內核。在仿真器坏境中,使用 zlmage, 可以替換 prebuilt 中的 prebuilt/android-ann/
目汞中的 kemel-qemu 文件, 即可以使用自己編诵出米的 Linux 內核。

�3.3.2 goldfish体系結枸移植
goldfish 灶理器有 ARMv5 和 ARMv7 两令版本,它們分別使用 arch/ann/configs/ 目汞中
的 goldfish_defconfig 和 goldfish_armv7_defconfig 作为其配置文件。
內核配置文件 goldfisb_defconfig 的 一 些片段如下所示:
CONFIG_ARM=y

# System Type

CONFIG ARCH GOLDFISH=y

# Goldfish Options
#
CONFIG MACH GOLDFISH=y
ff CONFIG MACH GOLDFISH ARMV7 is not set
CONFIG CPU ARM926T=y

由于 goldfish 虛姒灶理器厲于 ARM 体系結枸, 因此配置宏 CONFIG_ARM 需要被


使能 , 配置宏 CONFIG_ARCH_GOLDFISH 和 CONFIG_MACH_GOLDFISH 是 goldfish
灶理器迏癸机器使用的配置宏。 CONFIG_CPU_ARM926T 表示的实际上是 ARMv5 体系
結枸。
goldfish_armv7_defconfig 岂中使能的宏是 CONFIG_MACH_GOLDFISH_ARMV7, 没
有 CONFIG_CPU_ARM926T, 但使能了 CONFIG_CPU_V7、 CONFIG_CPU_VFP 等几令与
体系結杓相美的宏。 其他的配置部分, 二者則是完全相同的。
配置文件中的几介与 Android 系统相失的宏如下所示:

�38
第3章 Android的Linux內核和驱劫 ooe

# Android

CONFIG_ANDROID=y

#非 非 非 品 µ 非
CONFIG_ANDROID_BINDER_IPC=y Binder IPC驱劫程序
CONFIG_ANDROID_LOGGER=y Log记泵器驱劫程序
# CONFIG ANDROID RAM CONSOLE is not set Ram控制台
CON'FIG_ANDROID_TIMED_OUTPUT=y 定时榆出驱劫程序框架
CONFIG_ANDROID_LOW_MEMORY_KILLER=y 低內存汞死器
CONFIG_ANDROID_PMEM=y 物理內存驱劫程序
CONFIG_ASHMEM=y 匿名共享內存驱劫程序
CONFIG_RTC_INTF_ALARM=y


CONFIG_HAS」�AKELOCK=y 屯源管理相失的部分Wakelock和earlysuspend
CONFIG_HAS_EARLYSUSPEND=y
CONFIG」�AKELOCK=y
CONFIG_WAKELOCK_STAT=y
CONFIG_USER_WAKELOCK=y
CONFIG_EAR泣;SUSP回ND=y

配置文件中, 一些用于配置驱幼程序的宏如下所示:
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_GOLDFISfj=y

迏些驱幼程序实际上是 goldfish 系统虛姒没各的驱劫程序, 其中的姓理器也是虛姒的,


最終由仿真器坏境米实現。
``
goldfish 灶理器是 ARM 体系結枸的 一秤 机器"' 其代碼在 arch/arm/mach-goldfish/ 目
汞中, 其中主要的內容如下所示。
• board-goldfish.c: 板级主文件。
• timer.c: 定时器部分的实瑰。
• pm.c: 屯源管理部分的实現。
• include/mach//: 相美的失文件。
arch/arm/mach-goldfish/ 目泵中的 Kconfig 是 goldfish 的主配置文件, 內容如下所示:
if ARCH GOLDFISH
menu "Goldfish Options"
config MACH_GOLDFISH
bool "Goldfish (Virtual Platform)"
select CPU ARM926T
config MACH GOLDFISH ARMV7
bool "Goldfish ARMv7 (Virtual Platform)"
select CPU V7
select VFP
select VFPv3
select NEON
endrnenu
endif

ARCH_GOLDFISH 宏被使能的时候, 可以造掙两秤 goldfish 灶理器, 一秤由

39

• O o Android 板级支持与硬件相美子系统


MACH_GOLDFISH 表示, 一牙中由宏 MACH_GOLDFISH_ARMV7 表示。 前者 是 神
ARMv5E 体系結杓的 ARM926 灶理器, 后者是 A即'v1v7 体系結杓的処理器(即 Cortex A),
使能了 VFP 和 NEON 等特性。
arch/arm/mach-goldfish/ 目汞中 Makefile 的內容如下所示:
obj-y : = pdev bus.a timer.a switch.a audio.a pm.a
obj-$(CONFIG_MACH_GOLDFISH) += board-goldfish.a
obj-$(CONFIG_MACH_GOLDFISH_ARMV7) += board-goldfish.a

arch/arm/mach-goldfish/board-goldfish.c 是 goldfish 机器实現的核心文件,机器炎型的定


又如下所示:
MACHINE_START (GOLDFISH, "Goldfish")
.phys io = IO START,
.io_pg_offst = ((IO_BASE} >> 18) & Oxfffc,
.boot_params = Ox00000100,
. map io = goldfish map io,
. init_irq = goldfish init irq,
.init machine = goldfish_init,
.timer = &goldfish_timer,
MACHINE END

在 MACHINE_START 和 MACHINE_END 之阅的內容为机器的信息。迏里实瑰的結杓


是 arch/arm/include/asm/mach/arch.h 中定又的 struct machine_desc。 迏里賦值了定时器、 物
理 IO 等內容, 以及初始化机器、 初始化 irq 、 IO 映射等函敷指針。
在失文件中, hardware.h 定又了內存的基地址, irqs.h 定又了虛姒中新另。注意: 由于
goldfish 灶理器的各部分硬件都是虛姒的, 因此其洋细的"寄存器地址"在它亻J']各自的驱劫
实現中定乂, 而不像实际的硬件使用全局的定又。
pdev_bus.c 則是 一 令特殊的模掠没各注朋的文件, 它解決的是平台没各和平台驱劫的
匹配阅題。在其內部实現中启用了 一令紱程 (work), 在綫程循坏中自劫完成了各介没各的
注冊 (platform_device_register)。

�3.3.3 goldfish的相夫没各驱劫
goldfish 是虛姒姓理器, 因此其中的各令没各也是虛姒的, 懐取虛姒的寄存器地址, 并
使用虛姒的中斷, 具体的內容在仿真器的支持坏境中实珗。 迏些虛姒没各的驱劫程序的实
現方式, 大都基于 Linux 杯准驱劫程序的框架杓建。 很多虛姒没各使用虛姒的特殊功能寄
存器和中新。

1 . Framebuffer 的驱劫程序

goldfish 虛姒灶理器具有 Framebuffer (幀緩沖)的驱劫程序。 此驱幼程序的配置宏为


CONFIG_FB_GOLDFISH, 相美文件的路往为:如 vers/video/goldfishfb. c。
Framebuffer 的平台没各和平台驱劫的名林是 goldfish_fb, 在用户空冏的没各节燕为:
/dev/graphics/fbO 。 此驱幼使用 RGB565 作为颜色空岡, 虛姒瑰实的高为实际的高的两倍,
支持 pan 的操作。

�40
第3章 Android的Linux內核和驱劫 ooe

2. 鍵盆的驱劫程序
goldfish虛姒灶理器的榆入部分(犍盎)的驱劫程序是Input 驱劫蚩中的Event炎型。
此驱劫程序的配置宏为CONFIG_KEYBOARD_GOLDFISH_EVENTS , 相美文件的路往为:
drivers/input/keyboard/goldfish_events.c。
諭入部分的平台没各和平台驱劫的名林是goldfish_events, 在用户空向的没各节燕为:
/dev/input/ eventO。 此驱劫利用仿真器坏境从虛姒寄存器中荻取主机上的按鍵, 并 按照 一 般
的方法伟逸給系统的其他部分。

3. 实时时紳的驱劫程序
goldfish 虛 姒 灶 理 器 具 有 实 时 时 紳 的 驱 幼 程 序 。 此 驱 劫 程 序 的 配 置 宏 为
CONFIG_RTC_DRV_GOLDFISH, 相美文件的路往为: drivers/rtc/rtc-goldfish.c。
实时时紳平台没各和平台驱劫的名稔是 goldfish_rte, 其在用户空阅的没各节煮为

/dev/rtcO, 此节燕 般不使用。仿真器的
虛姒坏境蝕友中斷, 并填充相美的寄存器, 在 此驱
幼程序中取得信息, 并作为实时时钅中CRTC)的數据。

4. TTY 終端的驱劫程序
goldfish虛鉯灶理器具有TTY終端的驱劫程序, 也就是提供了虛姒 串口功能的驱幼程
序。 此驱劫程序的配置宏为CONFIG_GOLDFISH_TTY, 相美文件的路往为: drivers/char/
goldfish_tty.c
TTY平台没各和平台驱劫的名稔是goldfish_tty, 在用户空河有3介没各, 节煮 分別为
/dev/ttySO、/dev/ttySl 和 /dev/ttyS2。串口的功能比实际的串口功能要简单得多, 迸行的是直
接対虛姒寄存器的写操作 , 由仿真器坏境根据情況迸行灶理。

5. NandFlash的驱劫程序
goldfish虛姒灶理器的NandFlash 驱劫程序是柝准的MTD 驱劫程序。此驱劫程序的配
置宏为CONFIG_MTD_GOLDFISH_NAND, 相美文件的路往为: drivers/mtd/devices/, 其
中 goldfish _nand.c为实現文件, goldfish_nand_reg.c为
虛姒寄存器的定又文件。
NandFlash平台没各和平台驱劫的名稔是goldfish_nand, 并为每令分巨杓建字符没各和
坎没各。対 于同 一令分巨, 可能有两介字符没各分別用于渙/写和只涙。 此驱劫具体的功能
均由仿真器坏境根据內存的狀況米实瑰。

6. MMC 的驱劫程序
goldfish虛姒灶理器具有的 MMC/SD 卡的主机驱劫程序。 此驱幼程序的配置宏 为
CONFIG_MMC_ GOLDFISH, 相美文件的路往为: drivers/mmc/host/goldfish.c。
MMC平台没各和平台驱劫的名稔是goldfish_mmc, 根据 SD卡的情況其中的没各将被
沢別 , 仿真器坏境的SD卡使用映像文件模姒。

7. 甩池的驱劫程序
goldfish虛姒灶理器的屯池驱劫程序是Power Supply 驱劫。 此驱劫程序的配置宏为

41�
•oo Android 板级支持与硬件相美子系统

CONFIG_BAT TERY_GOLDFISH, 相美文件的路往为:如 vers/power/goldfish_battery.c。


屯池平台没各和平台驱劫的名林是 goldfish-battery, 対用户空冏的接口为 sys 文件系统。
驱劫程序中实瑰了渙取厲性等几令操作 , 通迥淩取虛姒的寄存器得到音前"屯池"的信息。

8. 音頻的驱劫程序
goldfish 虛姒灶理器的音颜驱劫程序是简易的驱劫程序。 此驱劫程序的配置宏为
CONFIG_ARCH_GOLDFISH, 相美文件的路往为:如 vers/皿sc/goldfish_audio.c 。
音颜驱幼平台没各和平台驱劫的名稔是 goldfish_audio, 其在用户空岡的没各节熹为
/dev/eac, 対其迸行涙/写分別表示呆音和放音。 在懷/写的时候, 通迥仿真器联系到主机的
音颜系统, 荻得声音的諭入流和榆出流。

9. qemu
`` "
QEMU 仿真器的部分功能也需要在內核中有 一 些特殊的 硬件 支持, 迏些支持也在
goldfish 姓理器的驱劫中, 两部分功能分別是 qemutrace 和 qemupipe 。
qemutrace 使用 CONFIG_QEMU_ TRACE 作为配置宏,源代碼是 drivers/misc/qemutrace/
目汞中的 qemu_trace.c 和 qemu_trace_sysfs.c 。 qemutrace 平台没各和平台驱幼的名稔为
qemu_trace, 它提供給用户空冏的接口为 Misc 没各, 没各节燕为 /dev/qemu_trace, 以及 sys
文件系统的接口 /sys/qemu_trace 。
qemu卫ipe 用于为特殊的没各提供非常快速的通信通道, 使用 CONFIG_QEMU_PIPE
作为配置宏, 源代碼是 drivers/皿 sc/qemupipe/目汞中的 qemu卫ipe.c。 qemu卫 ipe 平台没各
和平台驱劫的名稔为 qemupipe, 它提供給用户空冏的接口为 Misc 没各, 没各节煮为
/dev/qemu卫ipe 。

3.4 高通MSM平台的內核和驱幼

�3.4.1 平台概述
MSM 是高通 (Qualcomm) 的系列灶理器, 是 Android 的一 秤常用姓理器。 目前 MSM
主要包含了 MSM7k 系列外理器和 QSD8k 系列灶理器。 MSM7k 系列灶理器的內核是
ARMv6 体系結杓的 ARM l 1, QSD8k 系列灶理器的內核是 ARMv7 体系結杓的 Scorpion 。
Nexus One 手机使用的就是 QSD8250 的灶理器。

-提示: MSM7k 和 QSD8k 原本是两科純理器, 但它仞的 Linux 內核统林为 MSM 。

使用 git 下裁 MSM 內核的方法如下所示:


$ git c},one https://android.googlesour:ce.com/kernel/msm

迸入目杲, 并且切換到稔定的分支:
$ cd msm

�42
第3章 Android 的 Linux 內核和驱劫 ooe

MSM 的源代碼中包含通用和考用的版本, MSM 是其通用的內容, mahimahi 是 Nexus


One 手机使用的配置內容。
配置成 MSM 通用內容的方法如下所示:
$ make A_RCH=arm msm_de_tconfig�- config:

配置成 Nexus One 手机內容的方法如下所示:


$ make ARCH=arm mahimahi_defconfig .config

两者的主体內容奕似, mahimahi 主要使用了 CONFIG_ARCH_ QSD8X50 作为配置宏。

�3.4.2 体系結枸移植
mahimahi 平台的体系結杓移植部分主要在 arch/arm/mach-msm 目汞中。
board-mahimahi.c 文件是移植部分的核心, 机器定又部分如下所示:
MACHINE_START(MAHIMAHI, "mahimahi")
#ifdef CONFIG MSM DEBUG UART
.phys_io = MSM DEBUG UART PHYS,
.io_ pg_offst = ((MSM_DEBUG_UART_BASE)»18) & Oxfffc,
#endif
.boot__params = Ox20000100,
.fixup = mahimahi fixup,
.map_io = mahimahi map io,
.init_irq = msm」.nit_irq,
. init machine = mahimahi_init,
.timer = &msm_timer,
MACHINE END

迏是 ARM 平台的通用的移植格式, mahimah口nit() 函敖执行了主要初始化工作,


msm_init_i琿)函數完成中新部分的初始化, 在 irq.c 文件中实瑰, msm_timer 則在同目汞的
timer.c 文件中定辶 board-mahimahi-keypad.c、board-mahimahi-panel.c 等几令文件也是板级
实珗的 一 部分。
board-mahimahi.c 文件中坯定又了表示平台没各 (platform_device) 的各今結杓体, 用
于和驱幼程序中的平台驱幼 (platform_driver) 相匹配。

�3.4.3 没各驱劫程序
高通 MSM 系列灶理器及相矢板级的驱劫程序集中在几令重熹目汞中。
• driver/video/msm/: MSM 的幀緩沖, GPU 驱劫等。
• driver/serial/msm_serial* .c: MSM 的串口驱劫。
• driver/media/video/: MSM 的视颜方面的驱劫 (V4L2)。
• driver/net/wireless/bcm4329/: BCM4329 WIFI 的驱劫。
• driver/mtd/devices/msm_nand.c: MSM 平台的 Nand Flash 驱劫。
• driver/mmc/host/: MSM 的 MMC/SD 主控制器驱劫。
• driver/i2c/busses/: MSM 的 12C 惡鈛驱劫。
• driver/input/misc/gpio_* .c: 一些通辻 GPIO 杓建成的輸入没各驱劫。

43�
•oO Android 板级支持与硬件相美子系统

一些驱劫可以从 /dev/ 目汞中找到其没各节燕, /sys/bus/platform/devices/ 目汞中根据名稔


也可以找到平台没各的信息。

3.5 三星平台的內核和驱幼

�3.5.1 平台概述

三星 (SAMSUNG) 的平台灶理器也在 Android 手机中有所使用。 此灶的三星平台
般考指三星処理器的平台, 而不是三星公司所出的手机。
'' "
Exynos 由两介希腈语单讠司組合而米: Exyp nos 和 Prasinos, 分別代表 智能 与"坏

保 之意。三星公司 Cortex A8 核心的姓理器被杯为 Hummingbird (蜂舄),它使用 PowerVR
VXD370 作为视颜硬件解碼单元, PowerVR SGX540 作为罔形処理器, 支持 OPENGL
ES2.0/l.l 和 OPENVG。 S5PCI 10 用于智能手机。 就是 Nexux S 手机所使用的 S5PC110 灶
理器也被稔为 Exynos 3110。
Exynos 3110 灶理器和外围結杓如囷 3-2 所示。
,.,,...

L_�鬻f!..t.c I
匕巨
I 罩 31)./2D叫I, RP 丨

Exynos 3110
Co!1exA8 800驌1111GHz
WIIIEON

囝3-2
-
Exynos 3110処理器和外围結杓

Exynos 3110 使用的內核工程为 kernel/samsung, 使用 git 取得方法如下所示:


$ git clone https: 仫android.googlesource.com/kernel/samsung

三星 herring 平台可以迸行如下造拌和配置:
$ git checkout origin/android-samsung-2.6.35-gingerbread
$ make ARCH=arm her_rin__g_defconfig 、.config

使用 arch/arm/configs/ 目最中的 herring_defconfig 作为其配置文件, 也就是三星平台的

�44
第3章 Android 的 Linux 內核和驱劫 ooe

Nexux S 手机的配置文件。
Exynos 內核工程实际上対皮于 Exynos 4x 和 Exynos 5x 系列的姓理器,使用 git 取得和
造捅方法如下所示:
$ git -clone https: //android.googlesource. com/kernel/exynos
$ git checkout origin(andr�id-exynos-3.4

対 Exynos 4x 和 Exynos 5x 系列的灶理器迸行配置的方法分別如下所示:


$ make ARCH=arm exynos4_defconfig .config
$ make ARCH=arm exyriosS_defconfi9 .confi9

�3.5.2 体系結杓移植
以三星 herring 平台为例, 体系結杓移植部分主要包括几令目汞。
e arch/arm/plat-s5p/: 平台共用部分, CPU 、 中斷、 定时器、 屯源管理。
e arch/arm/mach-s5pv2 l 0/: 板级部分, mach-herring.c 为移植核心文件。
e arch/arm/plat-samsung/: 三星的全局没各。
mach-herring.c 文件中的机器定乂部分如下所示:
MACHINE_START (HERRING, "herring")
.phys io = S3C PA UART & OxfffOOOOO,
. io_pg_offst = (((u32) S3C_VA_UART)»18) & Oxfffc,
.boot params = SSP_PA_SDRAM + OxlOO,
.fixup = herring_fixup,
.init_irq = s5pv210_init_irq,
.map_io = herring map io,

. init machine = herring_machine_init,


.t1mer = &sSp systimer,
MACHINE END

以上是通近 MACHINE_START 宏所定又,針対一 介 ARM 平台的通用的移植格式,其


中定叉了地址和中新, 并由 herring_machine_initO 函數执行了主要初始化工作。
arcb/arm/plat-s5p/ 目呆中的 hr-time-rtc.c 文件定又了內核定时器和实时时紳的部分,
s5p_systimer 就是內核移植所需要使用的定时器。
此外, drivers/serial/ 目呆中的 samsung.c 和 s5pv210.c 是系列灶理器的串口驱劫。

�3.5.3 驱劫程序部分
三星系列灶理器的驱劫程序集中在几令重燕目呆中。
• drivers/gpu/pvr: PowerVR SGX 囹形灶理器的支持。
• drivers/video/samsung/: 三星的 Framebuffer 驱幼,建立 s3cfb 没各。
• drivers/mtd/onenand/: One Nand Flash 存儲器驱劫, 建立 s5pc110-onenand 没各。
• drivers/media/video/samsung/fimc/: 三星 Video 视颜榆出驱蒭建立 s3c-func.N 没各。
• drivers/media/video/samsung/jpeg_v2/: 三星的 JPEG 驱劫,建立 s3c-jpg 没各。
• drivers/media/video/samsung/mfc50/: 三星的 Multi Format Codec 驱劫,建立 s3c-mfc
没各。
• drivers/misc/samsung_ modemctl/: 三星的 Modem 控制驱劫。

45�
• O O Android 板级支持与硬件相美子系统

a sound/soc/s3c24xx/: 三星灶理器的音颜驱劫, 建立 soc-audio.O 没各。


a sound/soc/codecs/中的几令文件: 三星音颜 Codec 的姓理部分。
根据 Linux 的 sys 文件系统, 其中很多没各在/sys/bus/platform/devices/ 目呆中根据名稔
找到。 三星灶理器的內部没各常常以 s3c 或者 s5 等字符串为前綴。例如, s3c2410-wdt 为看
汀狗, s3c24l 0-rtc 为实时时紳, s3c2440-i2c.N 为各令 I2C 的控制器, s3c24xx-pwm.N 为各
令 PWM, s5pv210-uart.N 为各介串口。

3.6 德州仅器OMAP平台的內核和驱幼

�3.6.1 平台概述
OM战 (Open Multimedia Application Platform) 是德州仅器 (TI) 系列的灶理器, 是基
于 Android 系统的常用的几神外理器之一 。 OMAP 表示玕放式多媒体陘用平台, 是 一 科为
満足新 一代多媒体信息灶理及第三代元紱通信皮用玕岌出米的高性能、 高集成度嵌入式灶
理器。 OMAP 釆用一 神狓特的戏核結杓, 把控制性絞強的 ARM 灶理器和高性能低功耗的
DSP 核結合起米, 是 一 牙中升放式的、 可編程体系結杓。 TI 秈特的 DSP/BIOS 析, 允讠午玕友
者在 RISC 和 DSP 之伺优化地分配任各。

Dll:m,:,::
OMAP 44xx 灶理器(戏 ARM 核+DSP 核)及其外围結杓如圏 3-3 所示。

·•一._, OMAP 凶 '鸕
,,_一-.直直鸕量 l 匿
I lllllfflfflliff
l 檣囑 11晝曰
llmers, lnlEmJpt controller, mailbox
- -疇�· . -•-

8oot/secun1 ROM

囹3-3 OMAP 44xx姓理器(戏ARM核+DSP核)及其外围結杓

使用 git 下裁 omap 內核的方法如下所示:


$ git clone https://android.googlesource.corn/kernel/ornap

�46
第3章 Android 的 Linux 內核和驱劫 ooe

代碼合庠中包含了不同的分支:
$ git branch -r
origin/HEAD -> origin/master
origin/android-omap-3.0
origin/android-omap-panda-3.0
origin/android-omap-steelhead-3.0-ics-aah
origin/android-omap-tuna-3.0
origin/android-omap-tuna-3.0 一ics-mrl
origin/android-omap-tuna-3.0-jb-prel
origin/android-omap-tuna-3.0-mrO
origin/android-omap-tuna-3.0-mrO .l
origin/linux-omap-3.0
origin/master

几令以 android 玕染的分支为 Android 系统的內核, panda、 tuna 都是基絀板的名稔。



Nexus Galaxy 系统的硬件就是基于 tuna 板的,可以看做是 神硬件上的"继承美系 "o Nexus
Galaxy 系统使用了 TI OMAP 4460 的灶理器, PowerVR SGX 540 的囹形芯片, TI TWL6040

的集成芯片, 迏些都是 OMAP 系统的核心部分;而 Wifi 、 藍牙、 伟感器、 掇像失的內容,


則米自于姓理器之外的芯片。

�3.6.2 体系結杓移植
OMAP 灶理器的 Linux 移植部分主要涉及以下 3 介目汞。
a arch/arm/plat-omap/: OM 战平台部分移植。

a arch/arm/mach-omap2/: OM战灶理器部分的移植。

a arch/arm/plat-omap/include/: OMAP 平台的失文件目呆,其中包含两介子目呆: mach

和 dspbridge。 dspbridge 目呆表示 DSP 析, 用于 ARM 方面控制 DSP 。

mach-omap2/目汞中的 board-*.c 文件为板级別的配置文件, 其中 board-tuna.c 也就是


Nexus Galaxy 手机使用的板级配置, 机器定又方面的內容如下所示:
MACHINE_START(TUNA, "Tuna")
.boot_params = Ox80000100,
.reserve = tuna_reserve,
.map_io = tuna map io,
.init_early = tuna_init_early,

.init_irq = gic_init_irq,

. init_machine = tuna_init,
. t 1.mer = &omap timer,
MACHINE END

tuna_in瑱)为板级初始化函數, gic_ init_ irq() 为中新初始化函數, 在 omap4-¢bmmon.c 中

实現, omap_ timer 是內核系统定时器在 timer-gp.c 中的定又。



board-tuna.c 中坯包括了 些平台没各的定叉, 相栄的文件坯有 board-tuna-*.c 等。

�3.6.3 驱劫程序部分
德州仅器的 OMAP 系列灶理器的驱劫程序集中在几令重煮目呆中。

47�
•oO Android板级支持与硬件相美子系统

e driver/tty/serial/omap-serial.c: OMAP的串口驱劫。
e drivers/video/omap2/: OMAP的幀緩沖和晁示子系统的內容。
e drivers/media/video/: 掇像失和视颜諭出方面。
• drivers/i2c/busses/i2c-omap.c: OM战的l2C惡紱的驱劫。
e drivers/input/keyboard/: 鍵盎驱劫。
• sound/soc/omap/: OMAP的音颜驱劫。
e driver/mmc/host/: OMAP的MM C/SD主控制器驱劫。
• drivers/dsp/bridge: OMAP平台的DSP析驱劫。
e driver/gpu/: GPU囷形灶理器的驱劫。
在运行时 , 驱劫通常表珗/dev/目汞中的没各节熹和sys文件系统中的文件。

48

第4章
昱示系统

4.1 晁示系统概述

Android 的晁示系统是系统与用户交互界面部分最基本的功能,其功能是将矩陈形式幀
敖据晁示到用户可見屏幕上。
晁示系统対皮的底尼硬件通常是晁示没各, 例如 LCD 及 LCD 控制器、 VGA 榆出没各
等。 在 BSP 部分, 晁示子系统的驱幼程序通常是 Linux 的幀緩沖 (Framebuffer) 驱幼, 其
硬件抽象扆是 gralloc 硬件模坎。
晁示系统的本地框架扆的內容和 UI 庠及 Surface 部分密切相美, 可以将单 一的晁示介
原囹扆化, 并且坯将支持附加額外操作。 在上尼晁示系统提供系统囹形的榆出没各, 整介
系统的 GUI 榆出最終都通迥晁示系统完成。 在 Java 扆次, 各神控件的外戏和直接的囹形
接口的絵制都是通這晁示系统呈瑰出來的, 也提供了可以直接通迥晁示系统迸行榆出的
手段。
晁示系统的相栄內容如表4-1所示。
表4-1 昱示系统的相夫內容

Android的展次 晁示系统部分 描 述

硬件居次 晁示没各(LCD、 VGA) 包括控制器和外接的愉出没各

操作系统扆 Framebuffer等驱劫 Framebuffer是字符没各

本地的硬件抽象展 gralloc模坎 实現可能与OpenGL部分有矢系

本地框架展 UJ庠,Surface部分 其中包括Binder的!PC部分

Java框架扆 Surfece、 View等奕

Java尼的AP! 元直接的AP! SurfaceView可以用于戍用尼的昱示

晁示的榆出没各是其中最基絀、 最核心的部分。 晁示子系统与其他部分可能存在耦合


栄系, 例如 OpenGL 的加速部分。 根据硬件的不同, 晁示子系统和加速硬件的結合实現也
不相同。 晁示子系统越到上尼部分, 与 Android 系统的其他部分的耦合性就更強。 在 Java
• O O Android 板级支持与硬件相美子系统

尼以上的部分, 晟示需要与 GUI 系统窗口管理等机制結合。 实际上,在 Java 尼并元直接晟


示的接口, 相美的接口实际上只是晟示子系统的相美部分 。

4.2 晁示子系统緒枸

�4.2.1 恙体結枸
Android 晁示結枸的結杓在 Android l.x 版本和 Android 2.x 及之后版本的下扆实現有所
巨別。 Android l.x 版本的晁示系统没有狹立的硬件抽象扆, 而是在 libui 庠中直接调用
Framebuffer 驱劫程序 。 Android 2.x 的主要変化是增加了名为 gralloc 的硬件模玦作为 晁示系
统的硬件抽象扆。 gralloc 的原本含又是囹形没各的分配。
在 Android 2.x 及之后的版本中, 晁示系统的結杓如圏 4-1 所示。

android .view.View android .view.SurfaceV,ew

Java框架展 android. view.Surface

本地框架扆

SurfaceFlingerClient

llbul I GraphicBuffer 丨
I GraphicBufferMapper II GraphicBufferAllocatorl

默iAgralloc硬件模坎
(default.9ralloc .so)

亡玉l
Linux內核扆 Framebuffer
移植
或其他驱劫
謳立
囹4-1 晁示系统的結杓

Android 的晁示系统自下而上 , 包括以 下几令部分的內容。


(1)驱劫程序
晁示系统通常使用 Framebuffer 作为驱劫程序, 也可以使用変化的 Framebuffer 没各驱劫,
或者結合其他驱劫程序使用。
(2) 硬件抽象扆
晁示系统的硬件抽象屄 gralloc 模玦是 一 令杯准的硬件模玦, 其失文件的路往为:
hardware/libhardware/include/hardware/gralloc. h 。 gralloc 模玦是放置于 /system/lib/hw 中的劫
态庠 , 名稔为 gralloc.<hardware>.so 。

, 50
第4章 昱示系统 ooe

(3)本地框架扆
本地框架尼中的 UI 庠和 Surface 系统均和晁示部分相美, 其中也包含了晁示基本系统
和相美部分联系。
UI 庠的染文件路桎为: frameworks/base/include/ui/。 UI 庠源代碼路往: frameworks/base/
libs/ui/。
a frameworks/base/include/surfaceflinger/: SurfaceFlingerCLient 的失文件。
a frameworks/base/libs/surfaceflinger_client/: libsurfaceflinger_client 庠的源代碼。
_
a frameworks/base/services/surfaceflinger/: SurfaceFlinger 的实現。
(4) JNI 部分
提供 Surface 的給 Java 扆的作为接口。
a frameworks/base/core/jni/android_view_Surface.cpp: Surface 的 JNI 的代碼。
(5) Java 扆的 Surface 等炎
a frameworks/base/core/java/android/view/: 包括囹尼几介相美的炎。
其中主要为 android.view 中的 Surface 和 View 奕,前者是囹尼在 Java 尼中的反映,后
者是所有控件的基奕。 另有 一 介 SurfaceView 突, 可以用于上尼的直接囹展操作。

提示: Android 4.2 的 Ul 庠和 SurfaceFlinger 的內容在 frameworks/native/ 目汞中。

�4.2.2 核心結枸和 UI 庠

1. 基本結枸的定乂
`'
在 Android 2.3 及之后的版本中, 由于增加了対 NDK 中的本地皮用 访阅本地窗口"
的支持, 因此晁示方面部分的敖据結枸也被重新迸行了定叉。
晁示相美的結杓包含在框架扆的几介失文件中。
• framework/base/include/ui/android native buffer.h: 定又用于晁示的內存接口。
• framework/base/native/include/android/native window.h: 定又窗口。
几介文件的內容被框架尼使用, 也被基于 NDK 的皮用作为失文件使用。
android_native_buffer_t 表示晁示內存匡域,此結枸的信息为寛、高、每行數目 (stride) 、
格式和內存巨域的指針。 android_native_window_t 表示一 介 Android 框架扆中的窗口, 由
ANativeWindow 定又得到, 其中的几介函數指針为窗口的操作, 如下所示。
• setSwaplnterval: 交換內存。
• dequeueBuffer: 把內存从臥列取出, 如果没有內存将阻塞。
JockBuffer: 練住內存, 鋨住后才能修改迏坎內存。
• queueBuffer: 友送內存, 将其解頠并友送晁示。
• query: 査洵內存的狀态。
• perform: 执行其他的操作(可以自定叉的)。
• cancelBuffer: 取消从臥列取出內存的操作。
由 此可見, Android 系统中基本的窗口是 一介具有戏緩沖巨、 韐流晁示的窗口。 対于

51�
•oo Android板级支持与硬件相美子系统

Android框 架中窗口的 实現, 就是实 珗 android_native_ window_ t結杓(也就是


ANativeWindow)。

a+)提示: 此婕以字母A升去的內容, 未自Android 2.3版本升蛤为NDK的定乂。

2. UI庠

UI庠(libui)在Android晁示系统的作用是承上启下的, 主要功能是: 调用作为硬件


抽象尼 的gralloc模玦, 対上提供框架尼窗口的实珗。
其中的几令与晁示相美的失文件和源代碼文件如下所示。
• FramebufferNativeWindow.*: 直接调用gralloc模玦, 并初始化。
• GraphicBufferAllocator. *: 完成晁存的分配。
e GraphicBufferMapper.*: 完成晁存的映射。
• GraphicBuffer.*: 表示晁存。
FramebufferNativeWindow癸本身是继承ANativeWindow (相涅于继承了android
native_ window_ t), 表示 一 令Android中的本地窗口。
FramebufferNativeWindow的杓造函數的主体內容如下所示:
FramebufferNativeWindow::FramebufferNativeWindow()
: BASE(),·fbDev(O), grDev(O), mUpdateOnDemand(false) {
hw module t const* module;
if (hw get module(GRALLOC HARDWARE MODULE ID, &module) == 0)//打升gralloc模玦

int str這e;
int err;
err = framebuffer_open(module, &fbDev); II打升Framebuffer没各
err = gralloc_open(module, &grDev); II打玕gralloc没各
if (! fbDev I I !grDev) return; //有錯课, 則返回
mUpdateOnDemand = (fbDev->setUpdateRect != 0); II可造的setUpdateRect
//初始化 FIFO, 实际上就是 一介戏緩沖內存
mNumBuffers = 2; //戏緩沖: 有2玦內存
mNumFreeBuffers = 2;
mBufferHead = mNumBuffers 一 1;
II初始化 2介緩沖巨
buffers[OJ = new NativeBuffer(
fbDev->width, fbDev->height, fbDev->format, GRALLOC USAGE睏FB);
buffers[l] = new NativeBuffer(
fbDev->width, fbDev->height, fbDev->format, GRALLOC USAGE HW FB);
II从gralloc没各中分配內存
err = grDev->alloc(grDev,
fbDev->width, fbDev->height, fbDev->format,
GRALLOC_USAGE_HW_FB, &buffers[O]->handle, &buffers[O]->stride);
err = grDev->alloc(grDev,
fbDev->width, fbDev->height, fbDev->format,
GRALLOC USAGE睏FB, &buffers[l]->handle, &buffers[l]->stride);
II从framebuffer没各中荻得常籮
const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags;
const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydp訌
const_cast<int&>(ANativeWindow::minSwapinterval) = fbDev->minSwapinterval;
const_cast<int&>(ANativeWindow::maxSwapinterval) = fbDev->maxSwapinterval;
} else { LOGE("Couldn't get gralloc module"); )

�52
第 4 章 晁示系统 ooe
ANativeWindow::setSwapinterval = setSwapinterval; //为表示窗口的成贝賦值
ANativeWindow::dequeueBuffer = dequeueBuffer;
ANative吣ndow::lockBuffer = lockBuffer;
ANative阻ndow::queueBuffer = queueBuffer;
ANativeWindow::query = query;
ANativeWindow::perforrn = perform;

FramebufferNativeWindow 初始化迥程主要的內容分成以下几介步驟。
9 打升 gralloc 模玦, 并打玕 framebuffer_device_t 和 alloc_device_t 迏两介没各。
3从 framebuffer_device_t 没各中荻得晁示巨的寛和高颜色格式, 使用它伯建立表示
內存的 NativeBuffer 結杓。
3 从 alloc_device_t 没各中分配內存到 NativeBuffer 的句柄中。
9 荻得 framebuffer_device_t 没各中的其他信息。
3 賦值 setSwaplnterval 、 queueBuffer 、 dequeueBuffer 和 query 等函敷指針。
FramebufferNativeWindow� 中 setSwaplnterval 、 queueBuffer 和 query 等几介函數的实
瑰和 framebuffer_device_t 有密切的美系。 FramebufferNativeWindow 实际上是対 gralloc 模
玦的 -尼封裝, 向 Android 的本地代碼尼提供表示窗口的 android_native_window_t 結杓
(ANativeWindow)。
GraphicBufferAllocator. 癸用于晁示緩沖的分配, 它也打玕了 Galloc 模玦, 并调用了其
中的 alloc_device_t 没各, 其主要的內容如下所示:
status_t GraphicBufferAllocator::alloc(uint32一_t w, uint32_t h, PixelFormat format,
int usage, buffer handle t* handle, int32 t* stride) {
if (!w 11 !h) w = h = l; //対榆入的寃高迸行保护, 最小取值为1
status_t err;
if (usage & GRALLOC_USAGE_HW_MASK) {
err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);
} else {
err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride);
I
II省略部分內容
return err;

此灶使用 mAllocDev 的哭型为 gralloc 接口中定叉的 alloc_device_t, 岂调用者的參數具


有GRALLOC_USAGE_HW _MASK 杯志的时候, 由 alloc_device_t 调用分配 一 令內存, 否
則从軟件分配 一 介內存。
GraphicBufferMapper 哭用于晁示緩沖的映射, 其中调用 Galloc 模玦,在其中注朋了表
示晁示緩沖巨的內存:
status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) {
status_t err;
if (sw_gralloc_handle_t::validate(handle) < 0) {
err = mAllocMod->registerBuffer(mAllocMod, handle);
J else {
err = sw_gralloc_handle_t::registerBuffer((sw_gralloc_handle_t*)handle);

return err;

53�
• O O Android 板级支持与硬件相美子系统

mAllocMod 的炎型为 gralloc_module_t, 根据句柄的有效情況, 可以从 gralloc 模玦中


注冊 Buffer, 也可以从欬件注冊 Buffer。
GraphicBuffer 炎继承实現了 android_native_buffer_t, 因此它表示的是晁存。 在实瑰中
奕又调用了分配器 GraphicBufferAllocator 和映射器 GraphicBufferMapper。

�4.2.3 Surface本地部分

Surface 中晁示相美的部分实际上是 UI 庠中晁示內容的迸 步使用, 也就是利用晟示
'`
的没各杓建 囷扆 "0
SurfaceFlinger 部分的客户端 SurfaceFlingerClient 和实珗部分 SurfaceFlinger 釆取 Binder
IPC 的跨迸程通信方式。其中 SurfaceFlinger 运行于袖立的迸程或者运行于 Java 的服各迸程
(system_server), 而调用者通常运行于座用程序的迸程。

1 . SurfaceFlinger 庠的与噩示相夫接口
a
SurfceFlingerClient 的失文件中使用 Grap扣cBuffer 等結杓。 ISurface.h 中具有如下方式
的定又:
class ISurface : public IInterface {
virtual sp<GraphicBuffer> requestBuffer(int bufferidx,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage} = O;
virtual status_t setBufferCount(int bufferCount} = 0;


!Surface 奕实际上是 令基于 Binder 的接口, 需要下扆去实珗。 Surface.h 中定又的
Surface 炎使用 !Surface 炎。
表示晁示內存的突在經這多尼的封裝和实現后, 上尼能看到的主要就是 Surface。

2. SurfaceFlinger 庠的实瑰

在囹扆管理庠的 SurfaceFlinger 中, Layer、 LayerBuffer、 LayerDim 和 LayerBlur 几介


炎均表示吁目居"' 它亻I'] 实現不同囹扆的操作。

Layer 表示 令普通的囹屄, 其中的嵌套哭 SurfaceLayerBuffer 实际上就是 !Surface 的
实珗者。坯有晁存管理器 (BufferManager), 它的实現是以戏晁示緩沖的形式対 GraphicBuffer
迸行管理。
LayerBuffer 対 gralloc 模玦也有调用, LayerBuffer 奕表示一 令可以用于"推送 (push)"
的晁存罔屄。 LayerBuffer 的嵌套奕 SurfaceLayerBuffer 也是 !Surface 的实現者。
LayerBuffer 嵌套定乂的另 一 令癸为 Buffer, 其杓造函敖如下所示:
LayerBuffer: : Buffer: : Buffer(const ISurface: : Buffei:Heap& buffers,
ssize t offset, size t bufferSize)
: mBufferHeap(bu"ffers), mSupportsCopybit (false) {
NativeBuffer& src(mNativeBuffer);
src.crop.l = 0; II剪切巨域
src.crop.t = 0;
src.crop.r = buffers.w;
src.crop.b = buffers.h;
src.img.w = buffers.hor_stride ?: buffers.w; //巨域大小和颜色格式
src.img.h = buffers.ver_str 這e ?: buffers.h;

�54
第4章 昱示系统 ooe
src.img.format = buffers.format;
src.img.base = (vo這*)(intptr_t(buffers.heap->base()) + offset);
src.img. handle = 0;
gralloc_module_t const * module = LayerBuffer: :getGrallocModule();
if (module && module->perform) { //根据情況调用
int err = module->perform(module, //调用perform, 侍入參數
GRALLOC MODULE PERFORM CREATE HANDLE FROM BUFFER,
buffers.heap->heapID(), bufferSize, offset,
buffers.heap->base(),&src.img.handle};
mSupportsCopybit = (err == NO_ERROR);

迏里调用的perform 是gralloc_module_t的一 介可造实現的函數指針, 如果蚩前使用的


gralloc模玦中 实瑰了迏令 函數指針, 則在迏里调用 函數。 使用 gralloc 模玦中的特殊命令,
可以荻得加速效果。
Layer和LayerBuffer的功能交由上扆,它亻I']执行晁示的最終灶理。LayerDim和LayerBlur
部分則是两介奕似特殊的囹尼, 分別实現変暗和模糊的效果, 它亻[]都通迥调用OpenGL的
相皮接口迸行 特殊的效果灶理。

3. Surface自勺 JNI

Surface 的 JNI提供対android.view中 SurfaceSession 和 Surface 两介突的本地支持。


Surface的JNI封裝了Surface和SurfaceComposerClient两介奕。
android_view_Surface.cpp片段如下所示:
#include <surfaceflinger/SurfaceComposerClient.h>
#include <surfaceflinger/Surface.h>
const char* const kSurfaceSessionClassPathName
= "android/view/SurfaceSession"; // android.view.SurfaceSession
const char* const kSurfaceClassPathName
= "android/view/Surface"; // android.view.Surface
static JNINativeMethod gSurfaceSessionMethods(J = (
{"init", "()V", (vo這*)SurfaceSession_init }, II初始化Surface
{"destroy", "()V", (void*) SurfaceSession_destroy } , //銷毀Surface
{"kill", "()V" (void*)SurfaceSession_kill), II汞死Surface
};
static JNINativeMethod gSurfaceMethods[] = {
{"lockCanvasNative",
"{Landroid/graphics/Rect;)Landroid/graphics/Canvas;",
(void*)Surface_lockCanvas } , //繞住画布
{"unlockCanvasAndPost", "(Landroid/graphics/Canvas;)V",
(void*)Surface_unlockCanvasAndPost J, II解棟画布并没送
{"setLayer", "(I)V", (void*)Surface_setLayer J, II没置扆
{"setPosition", "(II)V",(void*)Surface_setPosition }, II没置位置
{"setSize", "(II)V",(void*)Surface_setSize J, II没置尺寸
II省略部分內容
);

SurfaceSession負靑Surface系统的初始化和銷毀。 Surface奕与Surface 本地奕相対皮,


封裝了各令接口函數,其中"lockCanvasNative"和''unlockCanvasAndPost ''是晁示的美鍵所在,
前者練住并絵制,后者解钅典并晁示。Surface的JNI的实現和android.graphics包中的Canvas
炎也有联系。

55�
•oo Android 板级支持与硬件相美子系统

�4.2.4 Java展的Surface的処理
Surface 的 Java 部分的內容在 android.view 包中, 提供了囹扆的底扆姓理、 全局管理和
在控件中使用等几介方面。 Suface 哭的核心內容完成対基本 UI 系统的支持, SurfaceView
突則提供給 Java 扆作为直接晁示內存的接口。

1. 核心內容
Android 框架扆中与 Suface 相美的几令核心文件如下所示。

e SurfaceSession.java: SurfaceSession 炎是 令內部奕, 具有 JNI 部分。
e Surface.java: Surface 奕是主要的奕, 具有 JNI 部分。
e SurfaceHolder.java: SurfaceHolder 接口, 表示 Surface 炎的封裝。

SurfaceSession 比絞简单, 是対本地內容的封裝, 用于刨建囹尼的"佘话", 实际上相


者于全局的初始化和銷毀。
Surface 既是 一令表示囹扆的句柄, 也是连接 Java 本地尼的主要手段, Surface 中核心
的方法 lockCanvas() 和 unlockCanvasAndPost() 根本上就是从本地实現的。
Surface 奕中定又几令常量:
public static final int FX_SURFACE_NORMAL = OxOOOOOOOO; II普通
public static final int FX_SURFACE_BLUR = OxOOOlOOOO; //透明模糊
public static final int FX_SURFACE_DIM = Ox00020000; II透明変暗
puJ坦ic static fi庄吐 fX_SURFACE-MASK = OxO_OOFOOOO;

迏几令表示囹尼奕型的常量和本地定叉的有対皮美系。 Surface 杓造函數中 int 奕型的


flags 參敖就表示了迏今敷值。 Surface 奕的杓造函敖不是 APL 在皮用程序尼使用 Surface
也不合通迥 new 得到, 而是通迥 SurfaceHolder 接口得到。
SurfaceHolder 接口的主要內容如下所示:
public interface SurfaceHolder {
public static final int SURFACE_TYPE_NORMAL "'" MEMORY_TYPE_NORMAL;
@Deprecated
public static final int SURFACE_TYPE_HARDWARE = MEMORY_TYPE_HARDWARE;
@Deprecated
public static final int SURFACE_TYPE_GPU = MEMORY_TYPE_GPU;
public static final int SURFACE_TYPE_PUSH_BUFFERS
= MEMOR'/._TYPE_PUSH_BUFFERS;
public Canvas lockCanvas(); //鋨住Surface
public Canvas lockCanvas(Rect dirty);
public void unlockCanvasAndPost(Canvas canvas); //解琰Surface
public Surface getSurface(); //荻得Surface

SU頤ACE_TYPE_NORMAL 奕型和 SURFACE_TYPE_PUSH_BUFFERS 等常量也和本


地所定叉的內容 (ISurfaceComposerClient.h) 具有対皮美系, 前者表示通常的困扆, 后者
是推送 Push Buffer 方式的囹屄。
Surface 几介奕提供了 Java 框架扆基本的窗口和控件(基奕为 View) 的支持, 也就是
基 本的晁示功能。 SurfaceSession 在 Java 服各庠的窗 口 管 理器中建立唯 一 的实例,

�56
第4章 昱示系统 ooe

SurfaceHolder 的实珗者則在ViewR oot�中 。

2. 相失的內容
android.view包中的WindowManager.LayoutParams哭包含了 內存癸型定叉和OO尼定
又的常量。 其中几介奕型的定又 如下所示:
public static final int MEMORY_TYPE_NORMAL = 0;
@Deprecated public static f工nal int MEMORY_TYPE_HARDWARE = 1;
@Deprecated public static final int MEMORY_TYPE_GPU = 2;
public static final int MEMORY TYPE PUSH BUFFERS = 3;
public static final int FLAG一_DIM_BEHIND = Ox00000002;
publi_s_ stati<:: final int FLAG_BLUR_BEHIND = 0�00000004;

此姓対皮 的哭型就是普通(Normal)、 透明変暗( Dim)、 透明模糊(B lur), 用于不同秤


突尼的姓理。在 皮用程序的杓建中,通迥Windows奕的 setFlags()方法, 可以将 一 介Activity
没置的窗口囹尼 没置为以上几介值。
android. view包的SurfaceView奕 是View继承者, 即 一 介控件 。SurfaceView提供了在
Java扆逐幀向Suface笈送晁示 內容的机制, 相沮于其中具有SU愆�ACE_
TYPE_ PUSH_
B U FFERS奕型的Suface实例。
SurfaceView內部具有一 介 mSurfaceHolder的实例,可采用如下方法得到SurfaceHolder:
publi_c SurfaceHolder ��etHolder () {}

迸而可以通迥SurfaceHolder荻得SurfaceView 中的Surface。SurfaceView在实現的近
程中, 內部依托于- 今窗口(!Window)米完成。

4.3 晁示 BSP 的結枸

Android 晁示部分的 BSP支持, 主要是硬件抽象展的 grallloc 模 玦 和 內核中的


Framebuffer 等驱劫程序 。
gralloc 的 含乂为Grap比csAllocation(囹形分配), 它位于框架尼的通用部分和晁示没
各的驱劫程序之冏, 作为晁示硬件 部分対上尼的唯 一 接口。
gralloc有一 令不同于其他硬件模玦的地方,系统中必須有一 令grallloc, 否則系统元法
正常启劫。Android升源代碼中已經实現的默从的 gr alloc模玦名稔为gralloc.default. so。 它
不仅是仿真器的实瑰, 也适用于使用柝准Framebuffer驱劫的平台。
在某介硬件平台上, 如果实瑰特定 gr alloc模玦 , 其 下尼的晁示没各就可以 是各秤癸型
的驱劫程序。例如, 対一 今杯准Framebuffer驱劫程序实現帶有优化的改劫, 增加一 些ioctl
命令來荻得額外的控制;通迥Android 的 pmem驱劫等方式荻得加速的效果 。

�4.3.1 Framebuffer驱劫程序
在 Linux中 , Framebuffer驱劫是 柝准的晁示没各的驱劫;対于PC系统 , Framebuffer
驱劫是晁卡的驱劫;対于嵌入式系统的 soc 灶理器 , Framebuffer通常作为其LCD 控制器
或者 其他 晁示没各的驱劫。

57�
•oo Android 板级支持与硬件相美子系统

Framebuffer 驱劫是一 令字符没各, 迏今驱劫在文件系统中的没各节熹通常是/dev/fbX。


每令系统可以有多介晁示没各, 使用/dev/fbO、/dev/fbl 等米表示。

提示: 在 Android 中, Framebuffer 驱劫的改各节克在 /dev/graphics 目汞中。

Framebuffer 驱劫是一 介字符没各, 主没各另为 29, 次没各弓逸增生成(由每介


Framebuffer 程序的注冊順序決定)。
Framebuffer 晁示驱幼的架枸如囷 4-2 所示。

文件接口调用(ioctVmmap/write)

/dev/fbX 用户空冏

具体Framebuffer驱劫 內核空冏
(实現struct fb_info)
调用 f I 注朋
I register_framebuffer
I

硬件
Framebuffer驱劫核心(fbmem.c)
操作
调用T +注朋
字符没各驱幼程序核心

硬件扆
晁示硬件(LCD硬件)

困4-2 Framebuffer晟示驱劫的架杓

Framebuffer 驱劫在用户空伺大多使用 ioctl、mmap 等文件系统的接口迸行操作, ioctl


用于荻得和没置信息, mmap 可以将 Framebuffer 的內存映射到用户空冏。 Framebuffer 驱劫
也可以直接支持 write 操作, 直接用写的方式榆出晁示內容。
Framebuffer 驱幼的主要失文件: include/linux/tb.h 。
Framebuffer 驱劫核心实現: drivers/video/fbmem.c。
Framebuffer 驱劫中核心的敖据接口是 fb_info, 在 fb.h 中定乂:
struct fb_info {
int node;
int flags;
struct fb 'var 'screeninfo var; //晁示屏変的信息
struct fb 'fix 'screeninfo fix; //晁示屏固定信息
II省略部分成贝
struct fb_ops *fbops; II frarnebuffer的操作指針集合
//省略部分成員
,
.
.
',

struct fb_info包含了Framebuffer 驱劫的主要信息, struct fb_var_ screeninfo 和 struct


fb_fix_screeninfo 是两 令相美的數据結杓。 通 常対陘 FBIOGET_VSCREENINFO 和
FBIOGET_FSCREENINFO 迏两介 ioctl 命令从用户空冏荻得的晁示信息。 fb_ops 表示対
Framebuffer 的操作, 由 一系列函數指針組成。

58
,
第4章 昱示系统 ooe

在具体的 Framebuffer 驱劫的实現中, 通常通近以下函敖迸行注冊和注銷:


extern int register_framebuffer(struct fb_info *fb info);
extern int unregister framebuffer(struct fb info *fb info);

Framebuffer 的 ioctl 命令在 include/linux/ 目呆的 fb.h 文件中定又, 如下所示:


#define FBIOGET_VSCREENINFO Ox4600 II荻得変化屏幕信息
#define FBIOPUT_VSCREENINFO Ox4601 II没置変化屏幕信息
-Jidefine FBIOGET_FSCREENINFO Ox4602 //荻得固定屏幕信息
#define FBIOGETCMAP Ox4604 //荻得映射內容
#define FBIOPUTCMAP Ox4605 //没置映射內容
#define FBIOPAN DISPLAY Ox4606 //调整晁示巨域_(虛姒晟示-实际晁示)

具体的 Frarnebuffer 驱劫需要定叉 - 今实現 fb_info 結枸、 实瑰 fb_ops 中的各令函數指


針。从驱幼程序的用户空伺迸行) octl 调用时,佘特換成调用其中的函數。具体的 Framebuffer
驱劫注冊后, 将佘自劫逸增荻得 一介次没各另。
在配置 Linux 系统时, Framebuffer 驱劫的配置迭璜是: Device Drivers=>Graphics
support。 Framebuffer 驱劫中也包含了文本模式、控制台、 启劫囹杯 (Bootup Logo) 等子造
項支持, 具体的 Framebuffer 驱劫由每 一 介平台支持。
Framebuffer 驱劫程序通常也支持用户空冏的写操作, 在系统中调试 Framebuffer 驱劫
程序, 可以使用直接写驱劫程序没各节熹的方式, 迏祥可以改変基本的晁示情況。
例如, 在 Android 中, 可以使用如下的方式在 Framebuffer 中荻得榆出效果:
# cat init > /dev/graphics/fbO

如果屏幕大小是 QVGA (640x480) 的, 按照以上操作, 由于 init 文件的大小大约占到



介屏幕的晁示緩沖的 1/3, 而 init 文件中的內容対于晁示是朵亂的, 因此可以视察到屏幕
上 1/3 的花屏巨域。

e 提示:如果蚩前 Framebuffer 驱劫程序使用戏緩沖盅示方式,将令看到屏磊中交替盅



示的帶有花屏和不帶有花屏的內容, 交替时f司就是戏緩沖刷新阅隔时阅。

迸 一 步坯可以使用 dd 命令迸行指定大小的操作, 如下所示:


#_dd if=/dey_/zero of曰/dev/graphics/fbO count= l bs= 256k

命令表示向 /dev/graphics/fbO 没各中写入 256k 字节为 OxO 的內容, 使用迏今命令可以


看到屏幕中的部分黑屏。

�4.3.2 gralloc硬件抽象展
gralloc 模坎作为晁示系统硬件抽象展, 其接口的內容在 gr叫oc.h 文件中定叉。 此內容
由实际的硬件抽象尼实瑰, 由 UI 庠调用。
gralloc.h 首先定又了· 一介硬件模玦和两介子没各的名稔, 內容如下所示:
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
#define GRALLOC HARDWARE FBO "fbO" // framebuffer没各和
#define GRALLOC_HARDWARE_GPUO "gpuO" // GP_U 没各

59�
•oo Android 板级支持与硬件相美子系统

其中, "gralloc" 为迏介硬件模玦的名稔, "tbO" 和 "gpuO" 分別表示 Framebuffer 没各


和 GPU (Grap扣cs Process Unit, 囹形灶理单元)硬件没各。
gralloc 硬件模玦是护展定又 hw_module_t 米完成的, 定又如下所示:
typedef struct gralloc_module_t {
struct hw module t common;
int (*registerBuffer)(struct gralloc_module_t const* module,
buffer handle t handle);
int (*unregisterBuffer) (struct gralloc_module_t const* module,
buffer handle t handle);
int (*lock) (struct gralloc_module_t const* module, buffer_handle_t handle,
int usage, int 1, int t, int w, int h, void** vaddr);
int (*unlock)(struct gralloc_module_t const* module, buffer_handle_t handle);
int (*perform)(struct gralloc一_module_t const* module, int operation, ... );
void* reserved_proc(7];
}. gralloc module t;

gralloc_module_t 是模玦的核心, 其中的几令函敖指針的功能如下所示。


• registerBuffer 需要在 alloc_device_t::alloc 之前被调用, 其參數癸型 buffer_handle_t
是內存指針; unregisterBuffer 用于注銷此內存。
e lock 用于访祠特定访祠緩沖巨, 在调用迏介接口时, 硬件没各需要結束渲染或者完
成同步, 其參數指定一 介巨域。 unlock 用于所有 buffer 改変之后被调用。
e perform 用于可护展的功能, Surflinger 佘调用它, 但是其实現是可迭的。
gralloc 和 Framebuffer 两秤没各的打升和失明使用几介靜态函敖表示, 如下所示:
static inline int gralloc_open(const struct hw_module_t* module,
struct alloc device t** device) (
return module->methods->open(module,
GRALLOC HARDWARE GPUO, ( struct hw device t**)device);

- - -
static inline int gralloc close (struct alloc device t* device) {
return device->common.close(&device->common);

static inline int framebuffer_open(const struct hw_module_t* module,


struct framebuffer_device_t** device) {
return module->methods->open{module,
GRALLOC HARDWARE FBO , (struct hw device t**)device);

static inline int framebuffer_close(struct framebuffer_device_t* device) (


return device->common.close(&device->common);

gralloc_open()、gralloc_close ()、framebuffer_open ()和 framebuffer_close ()迏 4 今函數


是 gralloc 模坎特定的 API 。 调用它伯分別用于打升和美 l沮GRALLOC_HARDWARE_GPUO
和 GRALLOC_HARDWARE_FBO 迏两令没各。 前者対成的没各为 alloc_device_t 結杓体,
''
后者対庶的没各为 framebuffer_device_t 結杓体。 它亻[]都 继承自" hw_device_t 結杓体。
alloc_device_t 没各的定又如下所示:
typedef struct alloc device t {
struct hw device t common;
int (*alloc)(struct alloc device t* dev, II分配, 以寬、 高、 颜色格式为參敷
int w, int h, int format, int usage,
buffer handle t* handle, int* stride);

�60
第4章昱示系统 oo•
int (*free)(struct alloc_device_t* dev, //秤放
buffer handle t handle);

} alloc device t;

alloc 函數指針用于分配 一 玦晁示內存,其參敖包括寛、高、颜色格式和 buffer_handle_t


癸型的句柄, free 函數指針用于秤放晁示內存。
framebuffer_device_t 結枸的定又如下所示:
typedef struct framebuffer_device_t {
struct hw device t common;
const uint32_t flags;
const uint32_t width; //寛
const uint32_t height; //高
const int stride; II每行內容
const int format; II颜色格式
const float xdpi; II x方向像素密度
const float ydpi; II Y方向像素密度
const float fps; II幀率
const int minSwapinterval;
const int maxSwapinterval;
int reserved[SJ;
int (*setSwapinterval)(struct framebuffer_device_t* window, int interval);
int (*setUpdateRect)(struct framebuffer_device_t* window,
int left, int top, int width, int height);
int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
int (*composi豆onComplete)(struct framebuffer_device_t* dev);
VO這* reserved_proc[SJ;
) framebuffer device t;

framebuffer_device_t 没各中定又了几介通用 的 晁 示內 存描述和几令函 數 指 針,


setSwaplnterval 用于交換晁示巨域; setUpdateRect 用于更新指定的 巨域, 迏介函數是可造
的; post 用于友送某 一 令 Buffer 到屏幕上。

4.4 晁示BSP的实現

�4.4.1 模姒器晁示系统的实瑰
模姒器使用的晁示系统的驱劫程序是 goldfish 的 Framebuffer 驱幼程序, 使用的硬件抽
象尼是默从的 gralloc 模玦。

1 . Framebuffer 驱劫程序
goldfish 虛姒灶理器的 Framebuffer 驱劫程序在內核的路往 drivers/video/goldfishfb.c 中
实現。 迏是 一今杯准 Framebuffer 的驱劫程序, 其实現的基絀主机的晁示器。
迏今驱劫程序的初始化工作的內容是 goldfish_fb_probe, 如下所示:
static int goldfish_fb_probe(struct platform_device *pdev) {
struct goldfish_fb *fb;
fb->fb.fix.type = FB TYPE PACKED PIXELS;
fb->fb.fix.visual = FB VISUAL TRUECOLOR;
fb->fb.fix.line_length = width * 2; // RGB565每介像素占用16位 , 2介字节

61�
•oo Android 板级支持与硬件相美子系统

fb->fb.fix.accel = FB_ACCEL_NONE;
fb->fb.fix.ypanstep = l;
fb->fb.var.xres = width; II实际晟示巨域
fb->fb.var.yres = height;
fb一>fb.var.xres_virtual = width; //虛姒晁示巨域(高度为实际的两倍)
fb->fb.var.yres_virtual = height * 2;
fb->fb.var.bits_per_pixel = 16;
fb->fb.var.activate = FB ACTIVATE NOW;
fb->fb.var.height = readl(fb->reg_base + FB GET PHYS HEIGHT);//埃取虛姒寄存器
fb->fb.var.width = readl (fb->reg base + FB GET PHYS WIDTH);
fb->fb.var.red.o£fset = 11; // ll,GB565的颜色空冏
fb->fb.var.red.length = 5;
fb->fb.var.green.offset = 5;
fb->fb.var.green.length = 6;
fb->fb.var.blue.offset = 0;
fb->fb.var.blue.length = 5;
framesize = width * height * 2 * 2; II晁示级沖巨的大小, 考忠每像索大小的虛姒巨域
II迸行內存映射
fb->fb.screen_base = dma_alloc_writecombine(&pdev->dev, framesize,
&fbpaddr, GFP_KERNEL);
//省略部分內容
fb->fb.fix.smem_start = fbpaddr;
fb->fb.fix.smem_len = framesize;
ret = fb set var(&fb->fb, &fb->fb.var); II 没圀參數
II省略部分內容
ret = request irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED, pdev->name, fb);
II省略部分內容
writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB INT ENABLE);
goldfish_fb_pan_display(&fb->fb. var, &fb->fb); II更新晁示
ret = register framebuffer(&fb->fb); II注朋驱劫程序
II省略部分內容


呈然基于主机的晟示实現, 但是対"硬件 的操作依然通迥寄存器完成。 goldfish 虛姒
処理器的 Framebuffer 驱劫程序实現了 RGB565 的颜色空伺支持,虛姒晁示的 y 为实际晁示
的两倍, 用于戏緩沖。 由于 RGB565 颜色格式每介像素占用两今字节, 又具有戏晁示緩沖,
因此晁示緩沖巨的大小为 widthxheightx2x2。 在用户空岡, 两介晁示緩沖巨的切換可以通
迥调用 ioctl 命令 FBIOPAN_DISPLAY來控制。

2. 默讠人的 gralloc 模玦的实珉


默从的 gralloc 模玦不仅可以給模姒器实現, 対于实現了杯准 Framebuffer 驱幼程序的
硬件系统, 也可以使用迏介模玦。
默从的 �ralloc 模玦实現的代碼路往为: hardware/libhardware/modules/gralloc/。
根据硬件抽象尼的接口特意, 实現分为 gralloc_module_t 部分、 framebuffer_device_t
部分、 alloc_device_t 部分。 其源代碼如下所示。
e gralloc.cpp: 实瑰 gralloc_module_t 模坎和 alloc_device_t没各。
e framebuffer.cpp: 实現 framebuffer_device_t没各。
e mapper.cpp: 实現 Buffer 操作等函敖。
默从的 gralloc 模坎的实瑰如囹 4-3 所示。

�62
第4章 昱示系统 oo•

J
aJJ
ngJ
a1

io
s(a

a11
eA
aJ

J3UJdeMSl

a1
oo=e


oo4

epdmg
sod
�S
用户空冏
內核空冏

囹4-3 默从的gralloc模玦的实珗

默 iA gralloc 模玦所使用的硬件没各只有 Framebuffer 驱劫程序 。 因此, 实际上不仅


framebuffer_device_t 没各基于 Framebuffer 驱劫程序 , 而且 gralloc_module_t 模玦中的
registerBuffer 接口和 alloc_device_t 没各中的 alloc 等接口都是通這伺接调用 Framebuffer 驱
劫程序实瑰的 。
gralloc.cpp 中实珗 gralloc_module_t 模 玦的是晁示模 玦接 口 , 其打玕函數
gralloc_device_open() 的內容如下所示:

int gralloc_device_open(const hw_module_t* module, const char* name,


hw device t** device) {
int status = -EINVAL;
if (! strcmp (name, GRALLOC_HARDWARE_GPUO)) II打升alloc_device_t没各
gralloc_context_t *dev;
dev = (gralloc_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = gralloc_close;
dev->device.alloc = gralloc_alloc; // alloc device t的alloc接口
dev->device.free = gralloc_free; // alloc device t的free接口
*device = &dev->device.common;
status = 0;
} else ( II打升framebuffer device t没各
status = fb_device_open(module, name, device);
J
return status;

gralloc_priv.h 中定叉的 private_ module_t 奕型的結杓体奕型护展了 gralloc_module_t 結


杓体, 其中第 11- 成贝指針 base 就是gralloc_module_t 哭型。
private_ module _t 結杓体的实瑰如下所示:

-
struct private " -一-- t HAL MODULE INFO SYM = {
module
-- - 磾 `量- - -

63

•oo Android 板级支持与硬件相美子系统

base: { II米自gralloc_module_t結杓体
common: {
tag: HARDWARE MODULE TAG,
version maJor: 1,
version minor: 0,
id: GRALLOC HARDWARE MODULE ID,
name: "Graphics Memory Allocator Module",
author: "The Android Open Source Project",
methods: &gralloc_module_methods
},
registerBuffer: gralloc register buffer, //模坎的几介函敖指針
unregisterBuffer: gralloc_unregister_buffer,
lock: gralloc_lock,
unlock: gralloc_unlock,
),
framebuffer: 0, II附加成贝
flags: 0,
numBuffers: O,
bufferMask: 0,
lock: PTHREAD_MUTEX_INITIALIZER,
currentBuffer: 0,
};

gralloc_register_buffer、 gralloc_unregister_buffer、 gralloc_lock 和 gralloc_unlock 几介函


敖是操作的执行者, 它伯在 mapper.cpp 中实瑰。
模抉的 register_buffer 实际上是通迥映射打玕 Framebuffer 没各的文件描述符米实瑰的。
而迏介文件描述符是在 framebuffer_device_t 打升后才得到的。
framebuffer.cpp 实瑰了 framebuffer_device_t 没各, 迏里使用了戏緩沖的实珗方式。
framebuffer_device_ t 没各的打升部分 fb_device_open() 的內容如下所示:

int fb_device_open(hw_module_t const* module, const char* name,


hw device t** device) {
int status = -EINVAL;
if (! strcmp(name, GRALLOC_HARDWARE_FBO)) {
alloc_device_t* gralloc_device;
status = gralloc_open(module, &gralloc_device);
fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));
dev->device.common.tag = HARDWARE DEVICE TAG;
dev->device.common.version = O;
dev->device.common.module = const_cast<hw_module_t*>(module);
dev->device.common.close = fb_close;
dev->device.setSwaplnterval = fb_setSwaplnterval;
dev->device.post = fb_post;

dev->device.setUpdateRect = O;
private_module_t* m = (private_module t*)module;
status = mapFrameBuffer(m); //映射framebuffer没各
if (status >= 0) { //填充framebuffer device t没各的各介內容
int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
const_cast<uint32_t&>(dev->device.flags) = O;
const_cast<uint32_t&>(dev-;>device.width) = m一 >info.xres;
const_cast<uint32一_t&>(dev->device.height) = m->info.yres;
const_cast<int&>(dev->device.str這e) = stride;
const cast<int&>(dev->device.format) = HAL PIXEL FORMAT RGB 565;
const_cast<float&>(dev->device.xdpi) = m->xdpi;
const_cast<float&>(dev->device.ydpi) = m->ydpi;
const_cast<float&>(dev->device.fps) = m->fps;

�64
第 4章 昱示系统 ooe
const_cast<int&>(dev->device.minSwapinterval) = 1;
const_cast<int&>(dev->device.maxSwapinterval) = 1;
*device = &dev->device.common;

return status;

fb _device_ope瑱)的功能就是初始化了 一 介 framebuffer_device_t 没各, 其中的主体部分


在mapFrameBufferLocked() 中实瑰, 如下所示:
int mapFrameBufferLocked{struct private_module_t* module)

II省略部分內容
char const * canst device_template[] = { //定叉 framebuffer没各
"/dev/graphics/fb%u","/dev/fb知u",0) ;
int fd = -1;
int i=O;
char name[64];
while ((fd== -1) && device_template巨l) {
snprintf(name, 64, device_template乜l, 0); II 得到没各的名稔
fd = open(name, O_RDWR, 0);
i++;

struct fb fix screeninfo finfo;


江(ioctl(fd, FBIOGET_FSCREENINFO, &finfo} == -1) return -errno;//固定屏菲信息
struct fb var screeninfo info;
江(ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno; //変化屏菲信息
info.reserved[OJ = 0; // 没翌変化屏幕信息中的几介敷据
info.reserved[l) = 0;
info.reserved(2) = O;
info.xoffset = O;
info.yoffset = 0;
info.activate = FB ACTIVATE NOW;
info.bits_per_pixel = 16; // RGB565的颜色空伺
info.red.offset = 11;
info.red.length = 5;

info.green.offset = 5;
info.green.length = 6;
info.yres_virtual = info.yres * NUM_BUFFERS; // NUM_BUFFERS == 2
uint32_t flags = PAGE_FLIP;
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { //重新没置変化屏幕信息
info.yres_virtual = info.yres;
flags &= -PAGE_FLIP;

if (info.yres virtual < info.yres * 2 ) { //虛姒緩沖巨尺寸不到实际緩沖巨两倍的情況
info.yres_virtual = info.yres;
flags &= -PAGE_FLIP;

if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno; //変化屏幕信息
int refreshRate = lOOOOOOOOOOOOOOOLLU /

uint64_t( info.upper_margin + info.lower_margin + info.yres}
* ( info.left margin + info.right_margin + info.xres )
* info.pixclock
);
if (refreshRate == 0) { refreshRate = 60*1000; } //刷新颜率为60Hz
江(int(info.width) <= 0 11 int(info.height} <= 0) { //默iA dpi为160
info.width = ((info.xres * 25.4f}/160.0f + O.Sf);

65�
•O o Android 板级支持与硬件相美子系统

info.height = ((info.yres * 25.4f)


/160.0f + O.Sf);

float xdpi = (info.xres * 25.4f)/ info.width; //荻得x和y的dpi
float ydpi = (info.yres * 25.4f)/ info.height;
float fps = refreshRate/ 1000.0f;
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) return -errno;
if (finfo.smem_len <= 0) return -errno;
module->flags = flags; //没置模缺的內容
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
int err;
size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);
module->framebuffer = new private_handle_t(dup(fd), fbSize, 0);
module->numBuffers = info.yres virtual/ info.yres; //數值一般为2
module->bufferMask = 0;
II迸行从没各中的內存映射
VO這* vaddr = mmap(O,, fbSize, PROT_READI PROT_WRITE, MAP_SHARED, fd, 0);
module->framebuffer->base = intptr_t(vaddr);
memset(vaddr, 0, fbSize);
return 0;

迏里实瑰的操作內容基本上是対 Framebuffer 驱劫程序的杯准操作,使用 RGB565 的颜


色空阅, 至少需要虛姒緩沖巨是实际晁示巨域的两倍(主要指 y 方向是两倍)。 另外, 刷新
率和 DPI (单位面积的像素敖目)的计算为 Android 系统服各, 它們是可造的內容。
post 是 framebuffer_device_t 没各实現中的重熹, 表示将某令緩沖巨 (Buffer) 晁示在
屏幕上, 迏介 post 的內容如下所示:
static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer){
if (private_handle_t::validate(buffer) < 0) return -EINVAL;
fb context t* ctx = (fb context t*)dev;
private handle t const* hnd =
reinterpret cast<private handle t const*>(buffer);
private ' module 't* m = reinterpret cast<private module户>(
dev->common.module);
if (m->currentBuffer) {
m->base.unlock(&m->base, m->currentBuffer); //解钅贝緩沖巨
m->currentBuffer = 0;

if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {


m->base.lock(&m->base, buffer,
private module t::PRIV USAGE LOCKED FOR POST,
0, 0, m->info.xres, m->info.yres, NULL);
const size_t offset= hnd->base - m->framebuffer->base;
m->info.activate = FB ACTIVATE VBL;
m->info.yoffset = offset/ m->finfo.line length; II没翌変化屏幕信息
if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) (
m->base.unlock(&m->base, buffer);
return -errno;

m->currentBuffer = buffer;
) else { II不支持,使用內存夏制的方法
void* fb vaddr;
void* buffer vaddr;

�66
第4章 昱示系统 ooe
//対內存巨域迸行操作: 踴定-艾制-解碳
m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY,
0, 0, m->info.xres, m->info.yres, &fb_vaddr);
m->base.lock(&m->base, buffer, GRALLOC USAGE SW READ RARELY,
0, 0, m->info.xres, m->info.yres, &buffer_vaddr);
memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
m->base.unlock(&m->base, buffer);
m->base.unlock(&m 一 >base, m->framebuffer);

return O;

优化的方法是通迥 Framebuffer 驱幼的 ioctl 命令 FBIOPUT_VSCREENINFO 米实瑰的,


实际上就是通迥改変屏幕信息中的 yoffset, 实現戏緩沖的切換。 从迏里的控制中可知, 各
秤上下文信息米自于保存在 private_module_t 中的成贝。
屏幕上晁示需要灶理器的晟示系统通迥硬件DMA涙取昱示緩沖巨的數据, 而在程序
中需要写晁示緩沖巨的致据。 为了避免迏两介步驟同时迸行, gralloc 灶理的方式就是: 頠
定 一 介, 写內容, 解钅典它;在钅典定期岡, 迏令晁示緩沖巨不能被硬件DMA荻取, 另 一介
緩沖巨被解钅典可以用于晁示到屏幕上。
gralloc.cpp 实瑰了的 alloc_device_t 没各几介部分, 主要的內容是 alloc 、 free 和 close
几令函數指針。
模坎的核心为实瑰 alloc_device_t: :alloc 的 gralloc_alloc 函數, 定叉的內容如下所示:
static int gralloc_alloc(alloc_device_t* dev, int w, int h, int format, int usage,
buffer_handle_t* pHandle, int* pStride) {
if (! pHandle I I ! pStride) return -EINVAL;
size_t size, stride;
int align = 4;
int bpp = 0;
switch (format) { II颜色空何的灶理, 计算每像素字节
case HAL PIXEL FORMAT RGBA 8888: II每介像素 4字节的情況
case HAL PIXEL FORMAT RGBX 8888:
case HAL PIXEL FORMAT BGRA 8888:
bpp = 4;
break;
case HAL PIXEL FORMAT RGB 888: II每介像素3字节的情況
bpp = 3;
break;
case HAL PIXEL FORMAT RGB 565: II每介像素2字节的情況
case HAL PIXEL FORMAT RGBA 5551:
case HAL PIXEL FORMAT RGBA 4444:
bpp = 2;
break;
default:
return -EINVAL;

size t bpr = (w*bpp + (align-1)) & -(align 一 1); 、 II根据行的情況迸行対*整理


size = bpr * h;
stride = bpr I bpp;
int err;
if (usage & GRALLOC_USAGE_HW_FB) ( II根据參敖迸行判斬
err = gralloc_alloc_frarnebuffer{dev, size, usage, pHandle);
} else { err = gralloc_alloc_buffer {dev, size, usage, pHandle); }
II省略部分內容

67�
• O O Android板级支持与硬件相美子系统

*pStride = stride;
return O;

迏 里核心部分是者调 用柝准包含了GRALLOC_ USAGE_HW_FB时 ,调 用 了


gralloc _alloc _framebuffer函敖, gralloc_alloc _
framebuffer函敖调用了framebuffer.cpp 中的
mapFrameBuffer函敖, 迸而调用了mapFrameBufferLocked。 实际上, 迏里的实現是欬件 实
現, 都需要映射Framebuffer没各。
在gralloc_alloc _buffer的实瑰中,调用ashmem_ create_region()使用ashinem (匿名共享
內存)分配了名杯为"grailoc-buffer 的
" 內存,迏是一 令純欬件的实現。

�4.4.2 Nexus One系统的实瑰


Nexus One的晁示系统的BSP由Framebuffer驱劫和重新实瑰 gralloc模玦組成。

1. Framebuffer驱劫程序

Nexus One的Framebuffer驱劫程序使用MSM平台统 的驱幼程序, 其主要文件入口
为drivers /video/msm/msm_fb.c。 同时, ar ch /arm/ma ch-msm 的 device.c 中定又了対皮的
platform_devi ce (mddi 1 、 mddi2 、 mdp)。 mddi (MobileDisplay Digital Interface)是一 神串
行惡紱, 用于连接L CD 、 mdp (MobileDisplay Processor), 是晁示的主模玦。

msm_fb.c中 实瑰的基本上是杯准的Framebuffer驱劫,默从使用RGB565的颜色空l 司,
使用两倍于实际晁示巨的內存作为虛姒晁示巨。 其建立的迥程如下所示:
static vo這setup_fb_info{struct msmfb_info *msmfb) {
struct fb_info *fb_info = msmfb->fb;
int r;
strncpy{fb_info->fix.這, "msmfb", 16);
fb_info->fix.ypanstep = 1;
fb_info->fbops = &msmfb_ops; // MSM的fb操作函敷
fb info->flags = FBINFO DEFAULT; //没覧默从耘涙
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.visual
_ = FB_VISUAL_TRUECOLOR;
fb_info->fix.line_length = msmfb->xres * 2;
fb_info->var.xres = msmfb->xres;
fb_info->var.yres = msmfb->yres;
fb_info->var.width = msmfb->panel->fb_data->width; //从LCD得到分辨率
fb_info->var.height = msmfb->panel->fb_data->height;
fb_info->var.xres_virtual = msmfb->xres;
fb info->var.yres_virtual = msmfb->yres * 2;
fb info->var.bits per pixel = 16;
fb_info->var.accel_flags = O;
fb_info->var.yoffset = 0;
if {msmfb->panel->caps & MSMFB CAP PARTIAL UPDATES) {// MSM的fb部分更新功能
fb_info->fix.reserved[O] = Ox5444;
fb_info->fix.reserved[l] = Ox5055;
fb_info->var.reserved[OJ = Ox54445055;
fb_info->var.reserved[l) = 0;
fb_info->var.reserved[2) = (uint16_t)msmfb->xres I
((uint32_t)msmfb->yres << 16);

fb_info->var.red.offset = 11; //默从为RGB565的颜色空冏


fb_info->var.red.length = �;

�68
第4章 昱示系统 ooe
fb_info->var.red.msb_right = 0;
fb_info->var.green.offset = 5;
fb_info->var.green.length = 6;
fb_info->var.green.msb_right = O;
fb_info->var.blue.offset = 0;
fb_info->var.blue.length = 5;
fb_info->var.blue.msb_right = 0;
mdp->set_output_format(mdp, fb_info->var.bits_per_pixel); //没笠MDP颜色格式
r = fb_alloc_cmap(&fb_info->cmap, 16, 0);
fb_info->pseudo_palette = PP;
PP [OJ = 0;
for (r = l; r < 16; r++)
PP[r] = Oxffffffff;

几今額外的ioctl命令在失文件include/linux/msm_mdp .h中定又, 如下所示:


i/define MSMFB IOCTL MAGIC 'm'
idefine MSMFB GRP DISP IOW(MSMFB IOCTL MAGIC, 1, unsigned int)
嵒define MSMFB.一 SLIT IOW(MSMFB
. . "IOCTL MAGIC,
- 2, unsigned int)

msm_fb驱幼程序相比杯准的Framebufer驱劫程序, 特殊的地方是增加了特定的ioctl,
迏部分主体的內容如下所示:
static int msmfb_ioctl(struct fb_info *p, unsigned int cmd, unsigned long arg) {
void user *argp = (void _user *)arg;
int ret;
switch (cmd) {
case MSMFB GRP DISP: II渭用rndp的部分米实現MSMFB_GRP_DISP
mdp->set_grp一_disp(mdp, arg);
break;
case MSMFB BLIT: II实現MSMFB_BLIT, 用于內存坎夏制
ret = msmfb_blit(p, argp);
break;
default:
return -EINVAL;

return O;

除了以上特殊的ioctl, MSM的Framebuffer驱劫程序并元其他特別之灶。

2. gralloc模玦的实瑰
Nexus One系统的 gralloc 模玦使用QSD8k系列灶理器实現的內容, 此模坎基于其
Framebuffer和pmem驱劫实瑰。 其代碼路往为: hardware/msm7k/libgralloc-qsd8k/, 目杯为
劫态庠gralloc.qsd8k.so。
其源代碼如下所示。
a gralloc.cpp: 实瑰 gralloc_module_t模與
framebuffer.cpp: 实現framebuffer_device_t没各。
a mapper.cpp: 通迥pmem实瑰了Buffer操作等函致。
a pmemalloc. *: 使用 pmem的分配器 。
a allocator. 丸 通迥pmemalloc杓建的分配器Allocator, 被gralloc_module_ t调用。
a gpu. *: 通迥pmemalloc实現 alloc_device_t没各。

69�
.。 Android板级支持与硬件相美子系统

QSD8k的gralloc模玦的实珗如囹4-4所示。

一:i�wllcpdm �s
(
EAJ�l
8I
8o

Ie

UJ
4

sod
0s dEM Sl
.
alloc-device
.
:t
• framebuffer device t
:,.一-r

用户空伺

內核
· 一·
空冏

三;寸

囹4-4 QSD8k 的 gralloc模抉实瑰

QSD8k的 alloc模玦的主要实現与默从的gralloc突似,
gr 主要是內存的分配和映射方
式不同, 主要改劫是增加了対pmem 的使用。 与默从的 alloc
gr 模玦相比, 主要巨別在于
alloc_device_t的alloc和free通迥使用 pmem实現, gralloc_module_t增加了 perfor m实瑰,
ator 突。
增加鋪助的alloc
gralloc卫riv .h中定叉的 private_module_t护展了gralloc_module_t結杓体,它就是QSD8k
中表示的gralloc模坎, 其中多了 一 令perfor m 函敖指針。
gralloc.cpp中定叉的模玦打升函敖如下所示:
int gralloc_device_open(const hw_module_t* module, const cha户 name,
hw device t** device) {
int status = -EINVAL;
if (!strcmp(name, GRALLOC HARDWARE GPUO)) { II打升alloc device t没各
const private_module_t* m = reinterpret_cast<const private_module_t*>(
module);
gpu_context_t *dev;
dev = new gpu context t(gpuContextDeviceDepsimpl, pmemAllocator,
pmemAdspAllocator, m);
*device = &dev->common;
status = 0;
} else { II打升framebuffer device t没各
status = fb_device_open(module, name, device);

return status;

在需要打升alloc_device_ t没各时, 此灶打玕的是 gp u.*中的实現gpu_context_t, 迏介


部分是 QSD8k的gralloc模玦与默从gralloc模玦最大的巨別。

70

第4 章 昱示系统 ooe

perform函數指針的实瑰为mapper.cpp中的gralloc_perform()函數, 其內容如下所示:
int gralloc_perform(struct gralloc_module_t const* module,int operation, ...) {
int res = -EINVAL;
va_list args;
va_start(args, operation);
switch (operation) (
case GRALLOC MODULE PERFORM CREATE HANDLE FROM BUFFER: {
int fd = va_arg(args, int);
size_t size = va_arg(args, size_t);
size_t offset = va_arg(args, size_t);
void* base = va arg(args, void*);
//验证需要 一 1- pm-;;;m buffer
pmem_region region;
if (ioctl(fd, PMEM GET SIZE, &region) < 0) { //调用pmem的ioctl命令
break;

native_handle_t** handle = va_arg(args, native_handle_t**);
private_handle_t* hnd = (private_handle_t*)native_handle_create(
private_handle_t::sNumFds, private handle t::sNumints);
hnd->magic = private handle t::sMagic; //保存內容到private handle t結杓
hnd->fd = fd;
hnd->flags = private handle t::PRIV FLAGS USES PMEM;
hnd->size = size;
hnd->offset = offset;
hnd->base = intptr_t(base) + offset;
hnd->lockState = private handle t::LOCK STATE MAPPED;
*handle = (native handle t *)hnd; II返回private handle t結杓
res = O;
break;

va_end(args);
return res;

GRALLOC MODULE PERFORM CREATE HANDLE FROM BUFFER 作 为特殊的



命令在 gralloc_perform()函數中实現。 此命令是 令可造的功能, 在SurfaceFlinger 中被调
用。 迏里的实瑰调用pmem驱劫荻得了內存的大小。
gralloc_register_buffer、 gralloc_unregister_buffer、 gralloc_lock和gralloc_unlock几介函
數是也在mapper.cpp中实現。 与默从的gralloc相比, 它亻n实际上是通迥pmem完成的。
framebuffer.cpp 中的framebuffer_device_t和杯准的实瑰基本相同, 主要体現在增加了
更多颜色格式的支持, 并且以RGBA8888作为默从的颜色格式。
其中post 功能的主要巨別体現在, 圭不支持戏緩沖时需要迸行內存夏制, 此姓不再调
用memcopy而是调用msm_copy_buffer米实現, 迏部分內容如下所示:
static int fb post(struct framebuffer_device_t* dev, buffer_handle_t buffer){
II省略部分內容
if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
II省略部分內容
) else {
void* fb_vaddr;
void* buffer_vaddr;


II_対內存巨域迸行操作: 铱定-及制(使用ms!TI_cop_y__buffer辶-解鋨

71
• O O Android 板级支持与硬件相美子系统

m->base.lock(&m->base, m->framebuffer, GRALLOC_USAGE_SW_WRITE_RARELY,


0, 0, m->info.xres, m->info.yres, &fb_vaddr);
m->base.lock(&m->base, buffer, GRALLOC_USAGE_SW_READ_RARELY,
0, 0, m一 >info.xres, m 一 >info.yres, &buffer vaddr);
msm_copy_buffer( II 使用的是內存及制的功能
m->framebuffer, m->framebuffer->fd,
m->info.xres, m 一>info.yres, m->fbFormat,
m 一 >info.xoffset, m->info.yoffset,
m->info.width, m->info.height);
m->base.unlock(&m->base, buffer);
m->base.unlock(&m->base, m->framebuffer);

return 0;

msm_copy_buffer 是平台单狓实現的內容, 基 于其 Framebuffer 驱劫的 ioctl 命令


MSMFB_BLIT 釆实瑰, 內容如下所示:
static void msm_copy_buffer(buffer_handle_t handle, int fd,
int width, int height, int format, int x, int y, int w, int h) {
struct {
unsigned int count;
mdp_blit_req req;
} blit;
private handle t *priv = (private_handle_t*) handle;
II 省略部分內容
if {ioctl(fd, MSMFB BLIT, &blit)) II 调用 MSMFB BLIT迸行坎夏制操作
LOGE("MSMFB_BLIT failed = 钅d", -errno);

msm_copy_buffer 迸行內存夏制的功能和 memcpy 癸似,但是它利用了 MSM 幀緩沖驱


幼的硬件机制米实現。 迏里使用的是 ioctl 的 MSMFB_BLIT 命令。
pmemallo.h 中定又的 PmemUserspaceAllocator 和 PmemK.emelAllocator 都继承自
PmernAllocator, 分別表示使用 pmem 的用户空阅和內核中的內存分配器。
gralloc.cpp 中具有如下定又:
static SimpleBestFitAllocator pmemAllocMgr;
static PmemUserspaceAllocator pmemAllocator(
pmemAllocatorDeviceDepsimpl, pmemAllocMgr, "/dev/pmem");
static PmemKernelAllocator pmemAdspAllocator(
pmemAllocatorDeviceDepsimpl, "/_sl.ev/2mem_adsp");

此姓的 pmernAllocator 和 pmernAdspAllocator 分 別是 PmemUserspaceAllocator 和


PmemKernelAllocator 的实例。 參數中亻夸入的没各节燕是辻其调用的部分。
为了提高性能, QSD8k 使用了基于硬件的 pmem 驱劫程序, 姓理晁存敖据, 取代了原
本通迥 ashmem 分配和管理的部分。 pmemallo.cpp 中用于分配用户空伺內存和內核空冏內
存的两介 alloc_pmem_buffer 函數就是通迥対 pmem 没各节燕的 mmap 完成的。

�4.4.3 Nexus S系统的实瑰


Nexus S 的晁示系统的 BSP 使用 Framebuffer 驱幼程序和默 -iA gralloc 模玦。
在 Nexus S 系统的/dev/graphics 中, 共用 5 令 fb 没各节煮, 其中 fbO 为主晁示部分所

, 72
第4章 昱示系统 ooe

用的没各节熹。 其源代碼文件为drivers/video/samsung/s3cfb.c。
drivers/video/samsung/中几介相芙的源代碼文件的含又如下。
�s3cfb_fund6x.c: 三星晁示控制器 (SAMSUNG Display Controller) 的寄存器接口。
�s3cfb_nt35580.c: nt35580 TFT 晁示屏的驱劫。
�s3cfb_tl2796.c: s6e63mOAMOLED 晁示屏的驱幼, 包括背光部分。
晁示屏均使用了 SPI 接口与三星外理器相连。 型另为GT-19020 的 Nexus S 系统使用了
AMOLED 的晁示屏, AMOLED (active-matrix organic light-emitting diode, 超级有源矩眸的
岌光二极管)是一 神晁示技木。 型另为GT-19023 的 Nexus S 系统使用了 Super LCD 作为晁
示屏。
s3cfb.c 文 件 中配置 FrameBuffer 驱 劫 的像素格式为使 用配置宏 CONFIG_FB_
S3C_NR_BUFFERS 釆确定晁存的致目,相美的代碼在 s3cfb_init_fbinfo() 函敖中,如下所示:
struct fb_fix_screeninfo *fix = &fb->fix;
struct fb_var_screeninfo *var = &fb->var;
var->xres_virtual = var->xres;
var->yres_virtual = var->yres * CONFIG' FB "'S3C"NR BUFFERS; II 盅姒的高度

在同系列的灶理器中 CONFIG_FB_S3C_NR_BUFFERS 的值为4, 也就是在豎直 (y)


方向上晁示內存板度的 4 倍。
另 一 介相美的結杓 s3c_platform_fb 在 arch/arm/plat-s5p/devs.c 文件中定叉:
static struct s3c_platform_fb default_fb_data _initdata = {
hf defined(CONFIG CPU S5PV210 EVTO)
.hw ver = Ox60,
#else
.hw ver = Ox62,
#endif
.nr wins = 5,
.default一_win = CONFIG_FB_S3C_DEFAULT_WINDOW,
.swap = FB_SWAP_WORD I FB_SWAP_HWORD,
};

其中定叉的成員 nr_wins 就是窗口的數目, 也就是所注朋的 Framebuffer 没各的致目。


因此 s3ctb.c 驱劫在 s3ctb_alloc_ framebuffer()等函數中, 据此实現了 5 令 Framebuffer 没各。

�4.4.4 Galaxy Nexus系统的实瑰


Galaxy Nexus 系统基于 OMAP 的 Android 4.x 平台。 Galaxy Nexus 的 maguro 板正是基
于 TI 的 Tuna 板,位于 soc 上的晁示系统基本相同,而晁示屏則是使用了三星的 S6E8AAO
MIPI 的控制器。 Galaxy Nexus 系统使用了 OMAP 平台中 Framebuffer 的驱劫程序和 OMAP
的 gralloc 模玦, 其屏幕的大小为 1280x720 。 Galaxy Nexus 使用的晁示屏則是三星的
S6E8AAO 屏。
晁 示 系统的板级別定又为: arch/arm/mach-omap2/board-tuna-display.c , 其 中包 括了
"s6e8aa0"和 "hdmi_panel" 两秤屏幕的定又, 手机直接使用的屏幕是前者。
OMAP 的內核代碼中 include/linux/omaptb.h 是 framebuffer 部分的失文件, 定又了各秤

73�
• 0 0 Android板级支持与硬件相美子系统

數据結杓以及以OMAPFB_玕失的各介特殊的ioctl另。
OMAP 灶理器的內核中 drivers/video/omap2/omaptb/目汞提供了晁示部分的驱劫程序的
核心部分, 包括以下几今文件 。
• omaptb-main.c: 定又了平台 驱劫 "omaptb", 实現Framebuffer没各 。
• omaptb-ioctl.c: 提供ioctl的各介 接口, 例如OMAPFB_MR
I ROR 等。
• omaptb-sysfs.c: 提供sys文件系统的接口。
omaptb-sysfs.c 实瑰了在 sys文件系统 中創建了 一 些文件, 用于 晁示和迸行控制 。 例如
可以执行以下的操作:
shell@android:/sys/devices/platform/omapfb/graphics/fbO $ cat phys_addr
acaOOOOO
shell@android:/sys/devices/platform/omapfb/graphics/fbO $ cat virt_addr
caOOOOOO
shell@android:/sys/devices/platform/omapfb/graphics/fbO $ cat virtual_size
720, 1280

mirror等几介文件則可写, 可以用于控制Framebuffer 驱劫程序 。


OM战灶理器的DSS的含又为 晁示子系统(Display Sub System)。 晁示子系统的庠 在
如vers/video/omap2/dss 目汞中, 主要包含了core.c、 manager.c、 display.c、 overlay.c、 dss.c、
omapdss.c、 dpi.c、 dispc.c和venc.c等文件,迏些 內容杓成了晁示 驱劫程序公用的"庠程序" 0
几令与晁示相美板级別的支持文件如下 。
• arch/ann/plat-omap/fb.c: Framebuffer部分的板级別 支持, 定又了平台没各 "omapfb"。
• arch/arm/mach-omap2/board-tuna-display.c: 晁示的板级定乂, 包括屏部分的內容。
平台没各和平台 驱劫匹配之后, omapfb-main.c将注朋3令Framebuffer没各, 也就是
在运行时/dev/grap扣cs/中的fbO、 fb l 和fb2三介Framebuffer的没各节燕。
S6E8AAO的LCD屏的驱劫路往为: drivers/video/omap2/displays/panel-s6e8aa0. c, 实現
了名稔为"s6e8aa0"的驱劫, 岂中包括 了背光等控制方面的內容。
Galaxy Nexus的gralloc模坎則使用OM战的实現,以二迸制的形式友布,放置在目柝
系统的/vendor/lib/hw目汞中, 名稔为 gralloc.omap4.so。

�74
第5章
用户榆入系统

5.1 用户榆入系统概述

Andro id的用户榆入系统的职靑是荻取用户行为,主要功能是荻取用户使用榆入没各迸
行操作的信息, 并将其交由系统的相栄部分迸行灶理。
用户榆入系统対皮的没各是用户諭入的硬件, 例如蝕摸屏、 鍵盎、 孰迹球、 鼠杯等。
在BSP部分,用户榆入系统的 驱劫程序通常是Linux中Event奕型的Input驱劫,在用户空
伺坯需要定制 一些配置文件。
用户榆入系统的本地尼次框架使用的EventHub作为內核空阅和用户空冏的通用接口。
用户榆入的內容被统 一封裝成特定的結杓, 并亻考送到Java扆次迸行灶理。Java昆次中具有
榆入事件管理器対榆入事件迸行分友上投灶理, 一些榆入的默从灶理在Java框架扆中, 敖
据伟送到Java的皮用程序居由各介皮用灶理。
用户榆入系统的相矢內容如表5-1所示。

表5-1 用户榆入系统的相夫內容
Android的屏次 用户愉入系统部分 描 述

硬件尼次 蝕摸屏、 犍盎、 孰迹球、 鼠柝 通常在赴理器的外部

操作系统展 Event癸型的Input驱劫 字符没各, 可以有多介

本地框架房和硬件接口 EventHub部分,配笠文件解析灶理,JN]部分 JNI部分失文件供NDK使用

Java框架展 KeyEveot、 MotionEvent和榆入管理器 榆入管理器运行于Java服各迸程


Java長的API KeyEvent和MotionEvent 通述View、 Activity等突访何荻取榆入事件

在用户榆入系统的姓理中, 要灶理的不是一介没各, 而是多秤奕型的多介没各。 各科


不同没各在軟件尼次, 被封裝成统 一 的結杓: KeyEvent用于表示按鍵事件, MotionEvent
用于表示移幼事件。 表示事件的結杓将被送入諭入事件管理器,Java框架扆和皮用展均可
按照相同的方式迸行灶理。
•oO Android 板级支持与硬件相美子系统

5.2 Android 2.3用户榆入子系统

�5.2.1 忌体結枸
Android 用户榆入系统的結杓比絞简单, 自下而上包含了驱劫程序、 本地庠灶理部分、
Java 癸対諭入事件的灶理、対 Java 程序的接口。 Android 用户諭入系统的結杓如囹 5-1 所示。

com.android.server
lnputManager

Java框架展

本地框架扆 lnputManager JNI

r·-----------------------------i
: ... I . • �I I:
,______________________________ :

Linux內核扆

圏5-1 用户榆入系统的結杓

Android 的用户榆入系统自下而上, 包括以下几介部分的內容。


(I) 驱劫程序
通常使用 Event 奕型的 input 驱劫程序, 在/dev/input 目汞中可以具有多介没各节燕,
対座多介榆入硬件。
(2) 配置文件及其姓理
包括 UI 庠中的几令部分。
a frameworks/base/include/ui/: UI 庠的失文件, 包括按犍定乂。
a frameworks/base/libs/ui/: UI 庠源代碼, 包括按鍵的灶理部分 EventHub。
(3) JNI
包括事件結杓部分和服各部分。
a frameworks/base/core/jni/android_view_KeyEvent.*: 按鍵事件的本地支持。
a frameworks/base/core/jni/android_view_ MotionEvent.*: 运劫事件的本地支持。
a frameworks/base/services/jni/com_android_server_InputManager.cpp: 諭入管理器部分。
(4) Java 框架居部分
包括諭入系统結杓和榆入管理器。

, 76
第5章用户輸入系统 ooe

諭入系统的結杓中包括 android.view 包中的 KeyEvent、 MotionEvent 等 几介炎,它亻I']也


作为系统的 API 。代碼路往为 frameworks/base/core/java/android/view/。
諭入管理器 lnputManager 奐賣 Java 扆的諭入管理,作为系统公共部分运行。
代碼路往为 frameworks/base/services/java/com/android/server/lnputManager.java。

提示:在一今运行 Android 系统中,InputManager 运行于系统服夯器迸程,而按犍


的事件运行于亙用程序迸程。

�5.2.2 本地框架的几介部分
諭入系统在 U1 庠中的部分是 Android 系统諭入的基絀,并与 Java 的內容具有対皮美
系。包括了几介部分內容 : Key*.*等文件用于按鍵結杓和灶理, EventHub.*文件用于事件
処理的核心,Input*.* 表示榆入方面的還緝灶理 。

1. 按鍵配置文件的処理
蝕摸屏和軌迹球上才及的是坐杯、按下、抬起等信息 。按鍵灶理的這程稍稍夏朵,从驱
劫程序到 Android 的 Java 屄收到的信息,鍵表示方式經近了两次特化,如表 5-2 所示 。鍵
扭描碼 Scancode 是由 Linux 的 Input 驱劫框架定乂的整敖癸型 。鍵扣描碼 Scancode 统迥 一
次 特 化后 ,形 成 按 鍵的柝签 KeycodeLabel , 是 一 介字符串的 表示形式 。 按鍵的杯签
KeycodeLabel 經述特換后,再次形成整敖型的按鍵碼 KeyCode。在 Android 皮用程序尼,
主要使用按鍵碼 KeyCode 米巨分。

表5-2 Android按鍵榆入的两次特化

三三三:;:�,, I:: 1:三三�£:'.:�l::K�:,


軟件模玦 哭型 讠兑 明

串 枚举值

KeycodeLabels.h :¼文件中定又按犍碼为整敖值的格式,以 KeyCode 枚举值表示,其內


容如下所示:
typedef enum KeyCode I
kKeyCodeUnknown = 0,
kKeyCodeSoftLeft = 1,
kKeyCodeSoftRight = 2,
kKeyCodeHome = 3,
kKeyCodeBack = 4,
II省略其他的按鍵碼, 按鍵碼表示为连维的整數
) KeyCqje;

迸而在定 又了 KeycodeLabels.h 中 定 又了从字符串到 整敞的映射失系,敷組


KEYCODES 的定又如下所示:
static const KeycodeLabel KEYCODES(] = { // {字符串, 整數)
{ "SOFT_LEFT", 1 } ,
{ "SO立瓩啤亡_]. ...l..,_

77�
• O O Android 板级支持与硬件相美子系统

{ "HOME", 3 },
{ "BACK", 4 },
{ "CALL", 5 },
{ "ENDCALL", 6 },
{ "0", 7 }, II各介數字按犍
{ "l", 8 },
{ "2", 9 },
{ "3", 10 } ,
II省略中冏按犍映射
{ "A", 29 } , II各介字母按犍
{ "B", 30 },
II省略中伺按犍映射
{ "MENU", 82 } ,
II省略中l可按鍵映射
{ NULL, 0 }
};

數組 KEYCODES 表示的映射美系, 左列的內容即表示按犍杯签 KeyCodeLabel, 右列


的內容 为按鍵碼 KeyCode (与 KeyCode 的啟值対皮)。 实际上, 在按鍵信息第二次特化时
就是将字符串炎型 KeyCodeLabel 特化成整敖 的 KeyCode。
KeycodeLabel 的 Flags 的定又如下所示:
static const KeycodeLabel FLAGS[]= {
{ "WAKE", OxOOOOOOOl) , II可以喚醒睡眠, 并通知服用扆
{ "WAKE_DROPPED", Ox00000002 }, II可以喚醒睡眠, 不通知皮用扆
"SHIFT", Ox00000004 } , II自劫附加SHIFT
"CAPS_LOCK", Ox00000008), II自劫附加CAPS_LOCK
"ALT", OxOOOOOOlO l, II自劫附加ALT
"ALT_GR", 0x00000020) ,
{ "MENU", Ox00000040 } ,
{ "LAUNCHER", Ox00000080) ,
{ "VIRTUAL", Ox00000100 }, //虛姒按鍵的柝枳
{ NULL, 0 }
};

KeycodeLabel 表示按鍵的附厲杯讠只。 android.view. KeyEvent 奕中的 KeyCode 枚举值与


之具有対皮 美系。 迏些定又和 input.h 中的定又相同。
KeyCharacterMap.h 定乂了 按鍵的字符映射美系。 KeyCharacterMap 奕中具有 get(),
getN umber() 、 getMatch()几令函數, 用于完成按鍵碼到字符的特換。 NUMERIC 、 Ql4 、
QWERTY 枚举值則代表几秤犍盎癸型的名稔。
KeyCharacterMap 主要将按鍵碼映射为文本可涙別的字符串(例如晁示的杯签等)。
KeyCharacterMap 是一 介鋪助的功能:按鍵碼只是一 介与 UI 元哭的整敖, 通常用程序対其
迸行捕荻灶理。 岂需要将按鍵事件 特換为用户可見內容时, 就 需要此部分的姓理了。
Java 框架扆的 android.view 包 中 的 KeyCharacterMap 癸表 示 相 同 的含又。
android.text.method 包中有各秤 Linstener 接口,可以直接監昕 KeyCharacterMap 相芙的信息,
包括 DigitsKeyListener (數字) 和 TextKeyListener (文本)等。
按犍碼和按鍵字符映射的內容是在代碼中实現的內容, 坯需要配合劫态的配置文件米
使用。 在实瑰 Android 系统时, 可能需要更改迏两秤文件。 KeyLayoutMap.cpp 奐賣解析灶
理 kl 文件, KeyCharacterMap.cpp 奐靑解析 kcm 文件。

�78
第 5 章 用户輸入系统 ooe

2. EventHub対没各的使理
EventHub是榆入系统的中柩,也是Android 系统 中直接和没各相美的文件,EventHub
包括以下几介方面的功能。
圖瘟洞/dev/input/目呆中的没各节燕。
Q 打玕Input 没各节燕,迸行poll和read操作。
.根据没各名稔打升按鍵布局文件(*.kl)。
0荻取榆入的原始事件(RawEvent)。
EventHub.h中定又了表示 原始没各 事件的RawEvent, 如下所示:
struct RawEvent {
nsecs_t when; II记汞时伺
int32 t deviceid; //榆入没各的ID
int32_t type; //榆入没各的哭型
int32 t scanCode; II扭描碼
int32_t keyCode; //按鍵碼
int32 t value; II數值
uint32_t flag, s; //杯志
};

EventHub.h中定又了表示 没各癸型的枚举值,如下所示:
enum {
INPUT DEVICE CLASS KEYBOARD = OxOOOOOOOl, II鍵盎没各
INPUT_DEVICE_CLASS_ALPHAKEY = Ox00000002, II帶有字母和敷字的鍵盎
INPUT_DEVICE_CLASS_TOUCHSCREEN = Ox00000004, //蝕摸屏
INPUT DEVICE CLASS TRACKBALL = Ox00000008, II軌迹球
INPUT_DEVICE_CLASS一TOUCHSCREEN_MT = OxOOOOOOlO, II多熹蝕摸的融摸屏
INPUT DEVICE CLASS DPAD = Ox00000020, II小鍵盎
INPUT DEVICE CLASS GAMEPAD = Ox00000040, II游戏犍盎
INPUT DEVICE CLASS SWITCH = OxOOOOOOBO, II升失没各
.
n
'
••

INPUT_DEVICE_是表示 没各奕型的枚举值,用每位表示 一 秤没各。Android 的榆入系


统统 一管理犍盎、 蝕摸屏等不同炎型的没各,奕型用整致表示。
EventHub在实瑰逍程中,通這!Notify的机制盟測/dev/input/目呆中的没各节燕。 迏秤
机制可以支持熱插拔的没各,苗基于 USB 、 藍牙连接 的鍵盎、 鼠才示等没各插入系统后,在
/dev/input/目呆中可以出現相皮的没各节燕,EventHub就可以打升迏些节燕迸行灶理。
getEvent()是EventHub 中的核心函數, RawEvent 奕型的參敖将作为函數返回的信息。
対于没各要调用poll()和read()函敖荻取其中的事件 信息。
根据没各名稔打玕按鍵布局文件也是EventHub奐蠻的另外 一方面的內容,此部分內容
在打玕没各的迥程中迸行姓理, 其邊緝为从/systern/usr/keylayout/ 中査找按鍵布局文件,如
果有名杯为<没各名>.kl 的文件,則打升垓文件,否則使用默从的qwerty.kl。

3. lnputManager相芙几令部分
UI庠中的InputManager負靑諭入 事件管理方面,迏部分內容与具体没各元美。迏部分
內容 需要封裝到Java扆。 以上的部分需要引用NDK的失文件: frameworks/base/native/

79

•oo Android 板级支持与硬件相美子系统

include/input.h。
UI 庠中的 InputManager 主要包括以下几介部分。
e Input.*: 輸入數据結杓和內容的封裝, 提供 KeyEvent 和 MotionEvent 奕型。
e InputManager.*: 榆入管理器, 対上尼的主要接口。
e InputReader.*: 榆入內容解析灶理, 也包括虛姒按鍵、 多魚蝕摸的姓理。
e InputDispatcher.*: 諭入事件的分友。
e InputTransport. *: InputChannel、 InputPublisber 、 InputConsumer 三介炎。
在实瑰近程中, InputManagerlnterface、 InputReaderlnterface、 InputReaderlnterface 是三
介所定叉的接口, InputManager、 InputReader、 InputDispatcher 三令癸分別是它亻l'1 的实現。
InputManager 和 InputChannel 是需要封裝到上展的內容。

�5.2.3 JNI

1. KeyEvent和MotionEvent 的 JNI

android.view 包中 的 KeyEvent 和 MotionEvent 两 令 癸具 有 JNI 部 分 , 分 別 是


frameworks/base/core/jni/ 目呆中的几丕文件。
ct KeyEvent: android_ view_KeyEvent.b 和 android_view_KeyEvent.cpp。
ct MotionEvent: android_ view_ MotionEvent.h和android_view_MotionEvent.cpp。
android_view_KeyEvent.h 定乂的対外的函敖如下所示:
extern jobject android_view_KeyEvent_fromNative(JNIEnv* env,
const KeyEvent* event); //本地到Java
extern void android_view_KeyEvent_toNative(JNIEnv* env,
jobject eventObj, KeyEvent* event); //Java到本地

两令函數分別将 一介本地的 KeyEvent 対象夏制成 Java 中的癸,并将 Java 中的 KeyEvent


対象夏制到本地。
android_ view_MotionEvent.h 定叉的対外的函敷如下所示:
extern jobject android_view_MotionEvent_ fromNative(JNIEnv* env,
const MotionEvent* event); //本地到Java
extern void android_view_MotionEvent_toNative(JNIEnv* env,
jobject eventObj, MotionEvent* event); //Java到本地
extern void android _view_MotionEvent_recycle(JNIEnv* env,
jobject eventObj); II回收Jav_a対象

三介函數分別将 一 今本地的 MotionEvent 対象夏制成 Java 中的哭, 将 Java 中的


MotionEvent 対象夏制到本地, 并回收 一 令 Java 中的 MotionEvent 対象。

般是不需要失文件的,此姓之所以具有染文件, 是因为需要在 NDK 中使用迏些
JN1
函數, 支持在本地皮用中直接使用事件。 本地成用是 Android 2.3 (API 级別为 9) 才玕始
具有的, 因此榆入事件也做出了相皮改劫。

2. lnputManager 的 JNI

com_ android_server_ lnputManager.cpp 为系统服各庠提供支持, 通迥调用 UI 庠中的

�80
第5章 用户輸入系统 ooe

InputReader、 InputDispatcher 和 InputManager 等米完成功能。


NativelnputManager 是 一
介 C丹的 实 瑰 奕 。 NativelnputManager 奕 中 包 含 一 介
InputManager 奕型的成見在实 瑰 的各介接 口 中対其 迸行了封 裝 调 用 , 并且实現了
InputReaderPolicylnterface 和 InputDispatcherPolicylnterface 两介癸。 因此, 此炎实际上包含
了榆入事件的涙取策略和分笈策略的职責, 前者包括了荻取信息、 迥濾事件等功能, 后者
包括了注入事件、 通知等功能。
在与本地的的交互近程中, 此灶使用反向调用 Java 展实現通知机制, 并直接访祠 Java
癸中的一 些成贝的值。

�5.2.4 Java展的部分

1. KeyEvent 和 MotionEvent 哭及其使用汤合

android.view 包中 KeyEvent 和 MotionEvent 两令奕都是 Android 系统的 API。二者均继


承自抽象奕 InputEvent, 其中包含的內容和本地展基本相同。
android. view 包的 View 癸具有相皮的接口:
public boolean onKeyDown (int keyCode, KeyEvent event) II鍵按下事件
public boolean onKeyUp (int keyCode, KeyEvent event) //犍抬起事件
public boolean onTouchEvent (MotionEvent event) //蝕摸事件
pub_lic boolean onTra�kba11Event (MotionEvent event) II軌迹球事件

如果没有特殊情況, 鼠杯事件奕似于孰迹球事件。 KeyEvent 奕中也可以得到扭描碼


(scancode) , 但是通常不迸行使用。
android.view 包 中坯包 括 表示榆 入 事 件臥列的 InputQueue 和表示榆 入通道的
InputChannel 两令突。

2. 服勞庠中的 lnputManager

com.android.server 包中的 lnputManager 負靑対用户榆入事件迸行灶理并迸行分友, 它


被窗口管理器服各 (WindowManagerService) 迸行调用, 运行于 Android 系统的 Java 服各
迸程 (system_server)。

lnputManager 主要是対本地內容的 些封裝, 其中自己实現的功能并不多。 窗口管理
器服各中具有 InputManager 唯 一 实例, 由此辻諭入事件的管理尽量狓立于窗口的管理。
InputManager 中的 一 些代碼在絞新的实瑰中已經不再推荐使用。 例如, 从 sys 文件 系
统的/sys/board_properties/路往中使用 virtualkeys.<没各>荻取虛姒按鍵的定又, 从 *.idc 荻取
榆入没各的校准信息。 它1fJ的功能已經被挪到本地屄实瑰。

5.3 Android 4.2 的用户榆入子系统緒枸

�5.3.1 忌体結枸
Android 4.2 的諭入系统的結杓严生了一 些変化, 迏些変化辻諭入公共部分和皮用程序

81�
•oo Android板级支持与硬件相美子系统

的榆入部分的分工更加明确。
Android 4.2 榆入系统的結杓如困5-2所示。

com.android.server

Java!匡架展

本地框架展

Linux內核扆

囷5-2 Android 4.2榆入系统的結杓

libinput是新引入的一 介庠, 代碼路往为: frameworks/base/services/input/, 其中的內容


将生成libinput.so, 作为榆入系统中 底扆最相美的一部分, EventHub 是其中的一部分, 用
于调用Event諭入没各。
libinput被InputManagerService的JNI所调用:
e frameworks/base/services/jni/com_android_server_ input_InputManagerService.cpp
InputManagerService运行于系统服各器迸程, 调用底扆并提供全局諭入管理 :
e frameworks/base/services/java/com/android/server/input/lnputManagerService.java
libandroidfw也是新引入的一 令庠, 含叉为Android的框架庠(Framework), 它完成按
鍵布局方面的姓理。 按鍵相美的內容由从前 版本的UI庠移到此灶。
统迥JNI的封裝后, 提供KeyEnvent和MotionEvent两介突的支持, 迏部分 內容运行
于皮用程序的迸程中 。

提示: Android 4.0就引入了libinput庠, 但没有InputManagerService。

�5.3.2 lnputManagerService的实瑰
在Android 4.x中InputManagerService是喻入系统的核心坏节,它本身在系统服各器岂

中, 包名为com.android.server.input。InputManagerService是集合榆入系统所有功能的 介
癸, 而从前的版本則由InputManager等几介癸完成対座功能 。

, 82
第5章 用户輸入系统 ooe

InputManagerService 者中的底扆功能交由本地虻理, 其中的几令方法如下所示:


private static native int nativeinit(InputManagerService service,
Context context, MessageQueue messageQueue); //初始化
private static native void nativeStart(int ptr); //升始
private static native int nativeGetScanCodeState(int ptr, //荻得扭描碼狀态
int deviceid, int sourceMask, int scanCode);
private static native int nativeGetKeyCodeS.tate(int ptr, //荻得按鍵碼狀态
int deviceid, int sourceMask, i卫! keyCqde);

几令 notify:XX攻)函啟用于通知,迏些函敖也可以被本地的代碼调用,充岂回调的功能。
其中的 一 介 notifyInputDevicesChangedO 函敖如下所示:
private vo這notifyinputDevicesChanged(InputDevice [ J inputDevices) {
synchronized (minputDevicesLock) {
if (!minputDevicesChangedPending) {
minputDevicesChangedPending = true;
mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED,
minputDevices).sendToTarget(); //分友的消息

minputDevices = inputDevices; II賦值新的没各

调用 Handler 友送消息的迥程, 也就是交由 InputManagerService 迸行灶理。 一介细节


是要首先向挂起没各岌送, 然后将參敷中亻考入的 InputDevice 敖組賦值給 mlnputDevices。
本地代碼在 com_android_server_input_InputManagerService.cpp 文件中, 其中包括大量
的 JNI 实現, 荻得按鍵碼狀态的函數如下所示:
static jint nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
jint ptr, jint deviceid, jint sourceMask, jint keyCode) {
NativeinputManager* im = reinterpret_cast<NativeinputManager*>(ptr);
return im->getinputManager()->getReader()->getKeyCodeState(
deviceid, uint32_t(sourceMask), keyCode);

用于通知 notifyInputDevicesChanged()的函數如下所示:
void NativeinputManager::notifyinputDevicesChanged(
const Vector<InputDeviceinfo>& inputDevices) {
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
jobjectArray inputDevicesObjArray = env->NewObjectArray(count,
ginputDeviceClassinfo.clazz, NULL); //得到没各敷組
if (inputDevicesObjArray) {
bool error = false;
for (size t i = O; i < count; i++) { II対没各循坏迸行灶理
jobject inputDeviceObj =
android_view_InputDevice_create(env, inputDevices.iternAt位));
//省略錯课灶理的內容
env->SetObjectArrayElernent(inputDevicesObjArray, i, inputDeviceObj};
env->DeleteLocalRef(inputDeviceObj};

if (!error) {
env->CallVoidMethod(rnServiceObj, //调用Java的方法
gServiceClassinfo.notifyinputDevicesChanged, inputDevicesObjArray);

83�
• O O Android板级支持与硬件相美子系统

env->DeleteLocalRef(inputDevicesObjArray);

checkAndClearExceptionFromCallback(env, "notifyinputDevicesChanged");

代碼中包括多没各的灶理,使用一介循坏遍历找到所有的没各。

5.4 用户榆入BSP 的結枸

移植Android的用户榆入系统, 主要的工作分成以下两介部分。
圖榆入(Input)驱劫程序
用户空阅中劫态配置的 kl和kcm文件
Android用户榆入部分的硬件适配尼就是 libui庠中的 EventHub, 迏部分是系统杯准的
部分。 因此, 在实瑰特定硬件平台的Android 系统的时候,用户榆入的硬件抽象扆通常情
況下 不做改変。 EventHub使用Linux柝准的 input没各作为榆入没各,其中又以实用Event
没各居多。 在迏秤情況下, 为了实瑰Android系统的榆入,也必須使用Linux杯准Input驱
劫程序作为杯准的榆入。
由于杯准化程度比絞高, 实瑰 用户榆入系统,在用户空冏 一 般不需要更改代碼。 唯 一
的情況是使用不同的 kl和kcm文件, 使用 按犍的布局和按鍵字符映射失系。

�5.4.1 Input驱劫程序
Input 驱劫程序是 Linux 榆入没各的驱劫程序,分成游戏杆(joystick)、鼠杯( mouse
和皿ce)和事件没各(Event queue)3 秤驱劫程序。 目前以 事件驱劫程序为主,作为通用
的諭入驱劫,可以支持犍盎、鼠杯 、蝕摸屏等多秤榆入没各。
Input驱劫程序的主没各弓是13 , 驱幼程序的没各另分配如下所示。
e joystick游戏杆: 0�31
e mouse鼠抵32�62
mice鼠杯: 63
.事件(Event)没各: 64�95
实际上,每 一秤 Input没各占用5位,因此每秤没各包含的介致是32介。
Event没各在用户空岡大多使用 read、ioctl、poll等文件系统的接口迸行操作,read用
于漾取喻入信息,ioctl 用于荻得和没置信息,poll 调用可以迸行用户空阅的阻塞。 對內核
有 按犍等中斬时,通迥在中斷中喚醒poll的內核实現,迏祥在用户空岡的 poll调用也可以
返回。
Event没各在文件系统中的没各节燕为: /dev/input/eventX。 主没各弓为13, 次没各弓
逸增生成, 为64�95, 各介具体的没各在皿SC、touch screen、keyboard等目汞中。
榆入没各驱劫程序的失文件: include/Iinux/input.h。
榆入没各驱劫程序的核心和Event代碼分別是: drivers/input/input.c 和 drivers/input/
evdev.c。

�84
第5章 用户輸入系统 ooe

Event榆入驱劫的架杓如囷5-3所示。

文件接口调用(ioctl/poll/read)

用户空冏

具体Event 驱劫 內核空r司
(实現struct input_dev)

调用
..
! 注朋

層 调用 !注冊
硬件
操作

字符没各驱劫程序核心

硬件展
鼠杯、鍵盎、蝕摸屏等硬件

囝5-3 Event没各驱劫的架杓

input.h中定又了input_dev結杓, 它表示Input驱劫程序的各神信息, 対于Event没各


分为同步没各、犍盎、相対没各(鼠杯)、絶対没各(蝕摸屏) 等。
input_dev中定又并臼納了各秤没各的信息,例如按鍵、相対没各、絶対没各、朵璜没
各、LED、声音没各、 強制反愤没各、玕美没各等。
struct input_dev {
const char *name; //没各名杯
const char *phys; II没各在系统的物理路往
const char *uniq; II统 一的ID
struct input_id id; II没各ID
unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; II事件
unsigned long keybit[BITS_TO_LONGS(KEY_CNT)); II按鍵
unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; II相対没各
unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; II絶対没各
unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)); II朵頊没各
unsigned long ledbit[BITS一TO_LONGS(LED_CNT)J; // LED
unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; II声音没各


unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
signed long swbit[BITS_TO_LONGS(SW_CNT));
nsigned int keycodemax;
II強制反懷没各
II升夫没各
II按犍碼的最大值
苧signed int keycodesize; II按鍵碼的大小
void *keycode; II按犍碼
int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
int (*getkeycode)(struct input dev *dev, int scancode, int *keycode);
struct ff_device *ff;
u6signed int repeat_key;
s\:ruct timer_list timer;
int sync;
int abs[ABS_MAX + 1 J;

85�
• O O Android 板级支持与硬件相美子系统

int rep[REP_MAX + 1);


unsigned long key[BITS_TO_LONGS(KEY_CNT));
unsigned long led[BITS_TO_LONGS(LED_CNT));
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
unsigned long sw[BITS_TO_LONGS(SW_CNT));
int absmax[ABS_MAX + 1); //絶対没各相栄內容
int absmin[ABS_MAX + 1);
int absfuzz[醞S」媯+ 1);
int absflat[ABS_MAX + 1];
int (*open)(struct: input_dev *dev); // Input没各相失的操作的函敷指針
VO 這(*close)(struct input_dev *dev);
int (*flush)(struct input_dev *dev, struct f斗e *file);
int (*event)(struct input_dev *dev, unsigned int type,
unsigned int code, int value);
struct input_handle *grab;
spinlock_t event_lock;
struct mutex mutex;
unsigned int users;
int going_away;
struct device dev; //表示没各的結杓和鮭表
struct list_head h_list;
struct list_head node;

在具体的 Event 驱幼程序的实珗中, 如果得到按鍵的事件, 通常需要通迥以下的接口


向上迸行通知, 迏些內容也在 input.h 中定又, 如下所示:
VO 這input_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value);
VO 這input_inject_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev,
unsigned int code, int value)
input event(dev, EV_KEY, code, ! !value); }
static inline void input_report_rel(struct input_dev *dev,
unsigned int code, int value)
{input_event(dev, EV_REL, code, value); }
static inline void input_report_abs(struct input_dev *dev,
unsigned int code, int value)
{input_event(dev, EV_ABS, code, value); }
static inline void input_report_ff_status(struct input_dev *dev,
unsigned int code, int value)
{input_event(dev, EV_FF_STATUS, code, value); }
static inline void input_report_switch(struct input_dev *dev,
unsigned int code, int value)
{input_event(dev, EV_SW, code, ! !value); }
static inline void input_sync(struct input_dev *dev)
{ input�vent(dev,_Ey_SY?q 1. SYt:J」�EPORT, _O); }

対不同没各內容的扳告均是通迥 input_eventO 函數來完成的, 但使用了不同參數。


在手机系统中經常使用的鍵盎 (keyboard) 和小犍盎 (kaypad) 厲于按鍵没各 EV_KEY,
孰迹球厲于相対没各 EV_REL, 蝕摸屏厲于絶対没各 EV_ABS 。
美于按犍敖值的定又片段如下所示:
#define KEY_RESERVED 0
#define KEY ESC 1
#defjne KEY 1 ____ 2

�86
第5章 用户輸入系统 ooe

#define KEY 2 3
tdefine KEY 0 11
#define KEY MINUS 12
lldefine KEY_EQOAL 13
#define KEY BACKSPACE 14
#define KEY_TAB 15
嵒define KEY_Q 16
itdefine KEY W 17
#define KEYE 18
#define KEY R 19
jdefin_e_KE_YT 20
'`
迏里用整敖杯沢各介按犍,迏秤敖值通常又被稔为 扭描碣 "0
getevent命令可以対Event没各迸行调试,执行命令行,所有使用Input没各的輸入 情
況将打出信息。 在Android的模姒器坏境中,使用getevent的情況如下所示:
ii getevent
add device 1: /dev/input/eventO
name: "qwerty2"
could not get driver version for /dev/input/mouseO, Not a typewriter
could not get driver version for /dev/input/mice, Not a typewriter
/dev/input/eventO: 0001 0002 00000001
Lde立inputLe迷mt Q_ :_ ___QQ_O 1._競0旦00000

煮市數字按鍵 1, 出瑰了上面的信息,0002是按鍵的扣描碼,00000001和00000000
分別是按下和抬起的附加信息。 最前面的 0001实际上是榆入没各的哭型。
使用getevent()可以最直接地荻得按犍的扭描碼,从硬件的源染确定底扆榆入没各亻耜逸
上米的信息。

�5.4.2 榆入配置文件
Android 榆入系统在用户空岡的內容,可以通迥配置文件迸行 配置 。
a kl (Keycode Layout)文件: 后綴名为kl的配置文件 。
a kcm (KeyCharacterMap)文件: 后綴名为kcm的配置文件。
系统默从 配置文件的路往为: sdk/emulator/keymaps/。
經迥灶理后,kl和kcm文件被分別放置在目才示文件系统的/system/usr/keylayout/目汞和
/system/usr/keychars/目采中。kl文件将被直接夏制到目杯文件系统中;由于尺寸絞大,kcm
文件放置在目杯文件系统之前,需要經迥圧縮灶理。
kl和kcm的文件名的含叉为<没各>.kl和<没各>.kcm。 其中 <没各>的含又是諭入没各

的没各名,対于某 今榆入没各,如果具有相皮没各名的 kl或kcm文件,将优先使用;如
果没有,将使用默从的配置文件 。

1. kl: 按鍵布局文件

kl 文件将被直接放在目才示系统的/system/usr/keylayout/目呆中 。 Android 默从提供的按



犍 布局文件主要包括qwerty.kl。另 令按犍 布局文件AVRCP.kl 用于多媒体的控制,ACRCP
的含又为AudioNideo Remote Control Profile。
qwerty.kl 为全犍盎的布局文件,是系统中主要按犍使用的布局文件,也是被默从使用

87�
•oo Android 板级支持与硬件相美子系统

的, 其片段如下所示:
key 399 GRAVE
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10
key 11
key 158

9

BACK WAKE DROPPED


key 230 SOFT RIGHT WAKE
key 60 SOFT RIGHT WAKE
key 107 ENDCALL WAKE DROPPED
key 62 ENDCALL WAKE DROPPED
key 229 MENU WAKE DROPPED
#省略部分按鍵的対成內容
key 16 Q
key 17 w
key 18 E
key 19 R
key 20 T
key 115 VOLUME UP
key 114 VOLUME DOWN

在按鍵布局文件中, 第1列为按鍵的扭描碼, 是 一 令整數值;第2列为按鍵的杯签,


是 一 介字符串, 完成了按鍵信息的第1次鈴化, 将整型的扭描碼特換成字符串癸型的按鍵
柝签。 第 3 列表示按鍵的 Flag, 帯有 WAKE 字符, 表示迏令按鍵可以喚醒系统。
扭描碼米自驱劫程序, 晁然, 不同的扭描碼可以対座 一 令按鍵杯签。 表示物理上的两
介按鍵可以対皮同 一 令功能按犍。
例如, 卉驱幼程序上亻考扭描碼为 158 的时候, 対皮的杯签为 BACK, 再统迥第二次特
換, 根据 KeycodeLabels.h 的 KEYCODES 數組, 其対皮的按鍵碼为 4 。
按鍵布局文件同时兼顾了 input 驱劫程序的定又和 Android 中按鍵的定叉。例如, Input
驱幼程序中定叉的數字扭描碼 KEY_l 的數值为 2, 迏里 2 対皮的按鍵杯签为" 1 "; Input
驱劫程序中定乂字母扭描碼 KEY_Q 的數值为 16, 迏里対皮的按鍵杯签为 "Q"。 然而各秤

Android 没各中使用到的全鍵盎华竟有不同之灶, 因此有 些按鍵与 input 驱劫程序的定乂
没有対皮美系。

2. kcm: 按鍵字符映射文件

kcm 表示按鍵字符的映射失系, 主要功能是将整敖癸型按鍵碼 (keycode) 特換成可以


晁示的字符。 kcm 文件将被 makekcharmap 工具特換成二迸制的格式, 放在目才示系统的
/system/usr/keychars/目汞中。
qwerty.kcm 是默从的按鍵字符映射文件, 表示全鍵盎字符映射, 其片段如下所示:
[ t ype=QWERTY]
# keycode display number base caps fn caps_fn
A 'A' '2' 'a ' 'A' 'j/' OxOO

88

第5章 用户輸入系统 ooe

'

,
'
,

'
'

'
,

,
22 3 3 3 4 4 4 5 5 5 6 6

b cd ef g hi ·

v 9 5 2 6 -[ $ ] " , ! >
BC D E F GH I J K L M N

BC D E F G H I JK L M N
B C DE F GH IJ K LM N

OxOO

,','·

',',',',',',
,',',',',','
',',',',',',

,',',','
',',',',

',',',',',',
',',',',',',
,',',',',','

,',',',',','
Ox00E7
OxOO
Ox0301
OxOOAS

�,',',',
' `
'{'
Ox0302

Jk l rnn
'}'

,','
',',
, -'
,、,
OxOO
Ox0303

第 一 列是特換之前的按鍵碼,第二列之后分別表示特換成为的晁示內容 (display) 、數
字 (number) 等。 迏些特化的內容和 KeyCharacterMap.h 中定又的 getDisplayLabel(),
getNumber()等函數相対氬
迏里的突型,除了 QWERTY 之外,坯可以是 Q14( 单鍵多字符対皮的鍵盎)、 NUMERIC
Cl2鍵的敖字鍵盎)等。

5.5 用户榆入BSP的实現

不同平台的鍵盎等榆入没各,通常是灶理器 soc 之外 的硬件。対于 Android 諭入系统


中的 BSP 支持部分的驱劫程序和配置文件,和姓理器的美系不大,而和其外部连接的硬件
美系絞大。

�5.5.1 模姒器中的实瑰

1. 驱劫程序
goldfish 虛姒灶理器鍵盎榆入部分的驱劫程序是 Event 驱劫程序,代碼路往为:

drivers/input/keyboard/goldfish_events.c。迏介驱劫程序是 介柝准的 Event 驱劫程序,在用
户空冏的没各节燕为/dev/input/eventO。
goldfish_events.c 中实現其核心的內容:
static irqreturn_t events_interrupt(int irq, void *dev_id)
struct event dev *edev = dev id;
unsigned type, code, value;
type = raw readl(edev->addr + REG READ); II哭型
code = ' 'raw readl(edev->addr + REG_READ); //碼
value = _raw_readl(edev->addr + REG_READ); II致值
input event(edev->input, type, code, value);
return IRQ HANDLED;

events_interrupt 实瑰的是按鍵事件的中斷灶理函敖,圭中斬友生后,埃取虛姒寄存器
的內容,将信息上才及。实际上,虛姒寄存器中的內容由模姒器根据主机坏境鍵盎按下的情
況得到。

89

•oO Android 板级支持与硬件相美子系统

2. 用户空阅的配置文件
在模姒器坏境中, 使用了默从的所有的 kl 和 kcm 文件, 由于模姒器坏境支持全鍵盎,
因此基本上包含了大部分的功能。 在模姒器坏境中, 实际上按鍵的扭描碼対皮的是桌面屯
胭的鍵盎(效果和鼠杯,屯市模姒器的控制面板奕似), 鍵盎的某些按犍按下后,若換为驱劫
程序中的扭描碼, 然后再由上尼的用户空岡灶理, 迏今迥程和实际系统中是癸似的。 晁然,
通迥更改默从的 kl 文件, 也可以更改实际按鍵的映射失系。
tuttle2.kl 和 tuttle2.kcm 是額外的按鍵布局和按鍵字符影射文件。

�5.5.2 Nexus One系统中的实瑰

1. 蝕摸屏、 執迹球和按鍵驱劫程序
Nexus One 系统使用 MSM 的 Mahimahi 平台的用户諭入没各包括以下几令和用户諭入
相美的 Event 没各。
e /dev/input/event4: 几令硬件的按鍵。
e /dev/input/event2: 蝕摸屏没各。
e /dev/input/event5: 孰迹球没各。
蝕摸屏的驱劫程序在 drivers/input/touchscreen 目汞中的 synaptics_i2c_rmi.c, 迏是 一 介
12C蝕摸屏的驱幼程序。
按鍵和軌迹球的功能, 具体的驱劫程序在 arch/arm/mach-msrn/ 目汞 board-ma届mahi­
keypad.c 文件中实瑰。
board-mahimahi-keypad.c 中的全局定又如下所示:
static struct gpio_event_info *mahimahi input info口 ={
&mahimahi_keypad_matrix_info.info, //犍盎矩眸
&mahimahi_keypad_key_info.info, 刀犍盎信息
&jogball_x_axis.info.info, //孰迹球x方向信息
&jogball_y_axis.info.info, //孰迹球Y方向信息
};
static struct gpio_event_platform_data mahimahi input data = {
.names = {"mahimahi 一 keypad", "mahimahi-nav", NULL, } , / /按鍵孰迹球的没各名稔
.info = mahimahi_input_info,
.info_count = ARRAY_SIZE(mahimahi_input_info),
.power = jogball_power,
};
static struct platform device mahimahi input device = { // GPIO的平台没各定叉
.name = GPIO EVENT DEV NAME,
.id = o,
.dev = { .platform_data = &mahimahi_input_data, } ,
};

按鍵和軌迹球是通迥 GPIO 系统米实現的, 因此定叉了 gpio_event_info 癸型的數組。


"mahimahi-keypad"和"mahimahi-nav"分別是两今没各的名杯。 gpio_event_info 哭型的指針敖
組 mahimahi_input_info 中包含了 mahimahi_keypad_matrix_info.画o, mahimahi_keypad_key_
info.info、 jogball_x_axis.info.info 和 jogball_y_axis.info.info。

�90
第 5章 用户輸入系统 ooe

按鍵驱劫是一 今利用 GPIO 矩眸的驱劫, 由 gpio_event_matrix_info 矩眸定又, 定又坯


需要包含按鍵的 GPIO 矩陈和 input 没各的信息, 內容如下所示:
static unsigned int mahimahi col gpios[) = { 33, 32, 31
static unsigned int mahimahi_row_gpios[) = { 42, 41, 40 };
#define KEYMAP_INDEX(col, row) ((col) *ARRAY_SIZE(mahimahi_row_gpios) + (row))
和define KEYMAP SIZE (ARRAY SIZE(mahimahi col gpios) *
ARRAY_SIZE(mahimahi_row_gpios))
static const unsigned short mahimahi keymap[KEYMAP SIZE) = II按鍵映射失系
[KEYMAP INDEX(O, 0)) = KEY VOLUMEUP, // KEY_VOLUMEOP == 115
[KEYMAP_INDEX{O, 1)] = KEY_VOLUMEDOWN, // KEY_VOLUMEDOWN == 114
[KEYMAP一INDEX(l, 1)] = MATRIX_KEY{l, BTN_MOUSE),
};
static struct gpio_event_matrix_info mahimahi keypad matrix info = {
.info.func = gpio_event_matrix_func, //矢犍函數实瑰
.keymap = mahimahi_keymap,
.output_gpios = mahimahi_col_gpios, // GPIO的行列实現
.input_gpios = mahimahi_row_gpios,
.noutputs = ARRAY_SIZE(mahimahi_col_gpios),
.ninputs = ARRAY SIZE(mahimahi row gpios),
.settle_time.tv.nsec = 40 * NSEC_PER_USEC,
.poll_time.tv.nsec = 20 * NSEC_PER_MSEC,
.flags = (GPIOKPF LEVEL TRIGGERED IRQ I
GPIOKPF REMOVE PHANTOM KEYS I GPIOKPF PRINT UNMAPPED KEYS),
);
static struct gpio event direct entry mahimahi keypad key map[] = { // Power按鍵
{ .gpio = MAHIMAHI GPIO POWER KEY, .code = KEY POWER, },
);
static struct gpio_event_input_info mahimahi_keypad_key_info = {
.info.func = gpio_event_input_func, II失犍函敷实瑰
.info.no suspend = true,
.flags = 0,
.type = EV_KEY,
.keymap = mahimabi_keypad_key_map,
.keymap_size = ARRAY_SIZE(mahimahi_keypad_key_map)
};

mahimahi_keypad_key_ matrix _info 和 mahimahi_keypad _info是gpio_event_matrix_info


奕型的結杓体, 分別奐賣两令和一 介按鍵的姓理, 实际上, MSM 的 Mahimahi 平台硬件上
只有三令按鍵: Power 、 音量增加和音量減少。 音量增加和音量減少的扭描碼分別是
KEY_VOLUMEUP (115)和KEY_VOLUMEDOWN Cl 14)。

- 提示:音量控制的两今按鐽在全鏈盎的 qwerty.kl 有所定乂, 同时符合 Linux 的 Input


波各和 Android 的按鏈的救值。

孰迹球也是由 GPIO 实現的, 由 X 方向和 Y 方向两部分組成, 內容如下所示:


static uint32_t jogball_x_gpios[) = {
MAHIMAHI GPIO BALL LEFT, MAHIMAHI GPIO BALL RIGHT, II左犍、 右犍
};
static uint32_t jogball_y_gpios[J = {
MAHIMAHI GPIO BALL UP, MAHIMAHI GPIO BALL DOWN, II上犍、 下犍
};
static struct jog axis info jogball x axis = { I I X紬的內容
.info = {

91�
•oo Android 板级支持与硬件相美子系统

.info.func = gpio_event_axis_func, //栄犍函致实現


.count = ARRAY_SIZE(jogball_x_gpios},
.dev = 1, .type = EV_REL, .code = REL_X, //相対没各, x紬
.decoded_size = lU << ARRAY_SIZE(jogball_x_gpios},
.map = jogball_axis_map, .gpio = jogball_x_gpios,
.flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,

);
static struct jog_axis_info jogball_y_axis = { II Y紬的內容
.info = {
.info.func = gpio_event_axis_func, II失犍函數实瑣
.count = ARRAY_SIZE{jogball_y_gpios)
.dev = 1, .type = EV_REL, .code = REL_Y, //相対没各, y紬
.decoded_size = lU << ARRAY_SIZE(jogball_y_gpios),
.map = jogball_axis_map, .gpio = jogball_y_gpios,
.flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION,

};

迏里的軌迹球是用 jog_axis_info 癸型的結杓体迸行定又的,迏神没各的哭型 (type)


是相対没各 EV_REL。

2. 用户空囘的配置文件
Nexus One 系统平台增加了 h2w_headset.kl 和 mahimahi-keypad.kl 作为按鍵布局的配置
文件,代碼的路禋为: device/htc/passion-common/。
h2w_headset.kl 文件的內容如下所示:
key 107 ENDCALL WAKE DROPPED
key 113 MUTE WAKE
key 114 VOLUME DOWN WAKE
key 115 VOLUME UP WAKE
key 163 MEDIA NEXT WAKE
key 164 MEDIA PLAY PAUSE WAKE
key 165 MEDIA PREVIOUS WAKE
key 226 HEADSETHOOK WAKE
key 231 CALL WAKE DROPPED

mahima证keypad.kl 与默从的 qwerty.kl 文件奕似,厲于全鍵盎的定叉,只是今別的鍵


值的內容有所不同。

3. 虛姒按鍵
虛姒按鍵将蝕摸屏辺緣作为按鍵的方式,在迏秤系统中 一 般蝕摸屏都比晁示屏略大,
在蝕摸屏下方多灶晁示屏的巨域,可以用作虛姒按鍵。
使用虛姒按鍵需要将鮋摸屏的巨域影射成按犍的坐杯,Android 的諭入系统要使用虛鉯
按犍定又文件。虛姒按鍵定又文件是/sys/board_properties/ 目汞中的 virtualkeys.<没各>文件。
迏是 sys 文件系统的文件,由內核中的代碼生成,被 Android 的榆入系统使用。
虛姒按鍵定叉文件的格式如下所示:
Oxl: 扭描碼: X:Y:W:H:Oxl: ……

例如 ,在 Nexus One 的系统上査看虛姒按鍵的配置文件如下所示:

�92
第5章 用户輸入系统 ooe

非 c-at Isys/board_properties/virtualkeys. synaptics-rmi-touchscreen


Ox01:158:55:835:90:55:0x01:139:172:835:125:55:0x01:102:298:835:115:55:0x01:217:412
:835 : 9
_ 5:55

迏里定又了4令按犍, 每令按犍的內容如下所示。
e OxO l:158:55:835:90:55: 最左 (90x55), Back 按鍵, 扭描碼 158。
e OxO l:139:172:835: 125:55: 中左 Cl25x55), Menu 按鍵, 扭描碼 139。
e OxO l:102:298:835:115:55: 中右 ( l15x55), Home 按鍵, 扭描碼 102。
龕 OxO l:217:412:835:95:55: 最右 (95x55), Search 按鍵, 扣描碼 217。
Msm 內核生成 sys 文件系统的文件是 arch/arm/mach-msrn/board-mahimahi.c。 它針対
synaptics-rrni-touchscreen 蝕摸屏的巨域生成的虛姒按犍文件。

�5.5.3 Nexus S系统中的实瑰

1. 驱劫程序
Nexus One 系统使用三星的 herring 平台的用户榆入没各包括以下几介和用户榆入相哭
的 Event 没各。
• /dev/input/eventO: mxt224_ts_input 蝕摸屏没各。
• /dev/input/event2: herring-keypad 鍵盎没各。
e /dev/input/event5: cypress-touchkey 虛姒按鍵, I2C 恙綫的地址为 10-0020。
herring-keypad 鍵盎驱劫的定又在板级移植文件 arch/arm/mach-s5pv210/mach-herring.c
中, 內容如下所示:
static struct gpio event platform data herring input data= {
. names = {"herring-keypad", NULL, } ,
.info= herring input info,
.info count= ARRAY SIZE(herring input info),
};

mxt224_ts_input 觔摸 屏 没各的代碼路 往 为: drivers/input/touchscreen/mxt224.c 。


mxt224.c 通逍控制 I2C 惡綫來实瑰蝕摸屏 Input 没各,也实珗了 - 今名为11 Atmel MXT22411
的 l2C 驱劫。
cypress-touch.key 是虛姒按鍵的没各, 驱劫实瑰的路往为 drivers/input/keyboard/ 目汞中
的 cypress-touchkey.c 和 cypress-touchkey-flITilware.c 文件, cypress-touchkey.c 中通辻涙取 I2C
惡綫荻得按犍的信息, 并且调用 input_report_key()等函敖向上展¥[扳, 其中坯实現了一 介
I2C 的驱劫模坎, cypress-touch.key-flITilware.c 則通迥 GPIO 迸行控制。没各相失的內容則在
板级文件 arch/arm/mach-s5pv210/mach-herring.c 中定又, 即 touch.key_platform_data結杓癸
型的敖据。

2. 用户空囘的配置文件
除了默从的文件外, Nexus S 系统增加了 cypress-touch.key.kl、herring-keypad.kl、
s3c-keypad.kl 和 secjack.kl 作为按鍵布局的配置文件, 路往为: device/samsung/crespo。
herring-keypad.kl 是平台的犍盎的配置文件, 內容如下所示:

93�
•oo Android 板级支持与硬件相美子系统

key 115 VOLUME UP WAKE


key 114 VOLUME DOWN WAKE
key 116 POWER WAKE
key 139 MENU VIRTUAL
key 102 HOME VIRTUAL
key 158 BACK VIRTUAL
key 217 SEARCH VIRTUAL

Nexus S 仅有屯源(用于升机美机)、 音量增加和音量減少 3 介硬件按鍵。


cypress-touchkey.kl 是按鍵鮋摸巨域的考用文件, 內容如下所示:
key 139 MENU VIRTUAL
key 102 HOME VIRTUAL
key 158 BACK VIRTUAI

key 217 SEARCH VIRTUAL

其中定叉的几介鍵值就是菜单、 桌面、 回退和搜索 Nexus S 晁示屏下方的按犍。

�5.5.4 Galaxy Nexus系统中的实珉



Galaxy Nexus 系统的榆入没各包括 介蝕摸屏, 仅有的 3 介硬件按鍵, 坯可以支持耳
机的紱控。 它亻i'J与灶理器的连接方式各自不同。

1. 驱劫程序
Galaxy Nexus 的几介諭入没各在內核中都表瑰成 Event 諭入没各, 如下所示。
e /dev/input/eventl : 名稔为 "Melfas MMSxxx Touchscreen" 的蝕摸屏。
e /dev/input/event2: 名杯为 "tuna-gpio-keypad" 的 GPIO 连接的鍵盎。
e /dev/input/event5: 名稔为 "Tuna Headset Jack"的耳机鈛控榆入没各。
蝕摸屏是连接在 OMAP 灶理器的第 3 令 I2C 恙紱上的没各,地址为 3-0048。其代碼路
往为:如 vers/input/touchscreen/mms_ ts.c, 在 sys 文件系统看到迏令没各的信息是:
shell@android:/sys/devices/platform/omap/omap_i2c.3八2c-3/3-0048 $ cat name
mms ts

GPIO 鍵盎的內容在 Tuna 板级支持的 arch/ann/mach-omap2/board-tuna-input.c 文件中定


又, 包括以下內容:
static struct gpio_event_direct_entry tuna_gpio_keypad keys_map_hig矼]={
{ .code = KEY POWER, .gpio = 3, } , // Power桉犍
};
static struct gpio_event」.nput_info tuna_gpio_ke'f!)ad_keys_info_high = {
.info.func = gpio_event_input_func, .info .no_suspend = true,

.type = EV_KEY, //为Event没各中的按犍炎型


.keymap = tuna_gpio_keypad_keys_map_high,
.keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_high),
.flags = GPIOEDF_ACTIVE_HIGH,
.debounce_time.tv64 = 2 * NSEC_PER_MSEC,
};
static struct gpio_event_direct_entry tuna_gpio_keypad keys map low[] = {
{ . code = KEY VOLUMEDOWN, .gpio = 8,) , //两介音嶽按鍵
{ .code = KEY VOLUMEUP, .gpio = 30,),
);
static struct gpio_ev�n�_i,_nput_info tuna_gpio_keypad_keys_info_low = {

�94
第5章用户輸入系统 ooe
.info.func = gpio_event_input func, .info.no suspend = true,
.type = EV_KEY, //为Event没各中的按犍哭型
.keymap = tuna_gpio_keypad_keys_map_low,
.keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_low),
.debounce_time.tv64 = 2 * NSEC_PER_MSEC,

static struct gpio_event_info *tuna_gpio_keypad_info[] = { // GPIO的事件的定又


&tuna_gpio_keypad_keys_info_high.info, &tuna_gpio_keypad_keys_info_low.info,

static struct gpio_event_platform_data tuna gpio keypad data = {


.name = "tuna-gpio-keypad兀 //驱劫的名杯
.info = tuna_gpio_keypad_info,
.info_count = ARRAY_SIZE(tuna_gpio_keypad_info)

其中包括了音量增加鍵、音量減少按犍和屯源按鍵 (Power) 的定又,它亻I] 都是连接在


GPIO 之上的。在驱劫程序中,使用 gpio_event_input_info 結杓直接将其实現成 Event 没各。

2. 用户空阅的配置文件
Galaxy Nexus 系统平台增加了 tuna-gpio-keypad.kl 为 Tuna 平台的鍵盎布局文件 ,文件
碼路往为 device/samsung/tuna/ tuna-gpio-keypad.kl, 內容如下所示:
$ cat tuna-gpio-keypad.kl
key 114 VOLUME_DO卵 WAKE
key 115 VOLUME UP WAKE
key l,16 POWER _ WAKE

迏里的 3 介按鍵均有 WAKE, 表示 Galaxy Nexus 的 3 介硬件的按鍵均可以喚醒。


文件 sec_jack.kl 用于耳机上面的紱控按鍵,內容如下所示:
key 163 MEDIA_NEXT WAKE
key 165 MEDIA一PREVIOUS WAKE
key 226 HEADSETHOOK WAKE

此灶的 3 介 按 鍵分別定叉为 KeycodeLabels.h 中的 87 ( "MEDIA_NEXT") 、 88


("MEDIA_pREVIOUS") 和 79 ("HEADSETHOOK") 3 今按犍碼。
另 一 令鍵盎布局文件 sii9234_rep.kl 用于 HDMI 控制芯片 MHD Sil9234。其中包括小犍
盎 (N口汨>AD) 和多媒体控制的各今按鍵。

95�
第6章
侍感器系统

6.1 伟感器系统概述

Android的亻考感器系统是系统荻取信息的手段, 从各秤不同哭型的佳感器硬件中取得,
并交由Android系统的相矢部分迸行灶理。
伟感器系统使用的硬件主要为各神亻耜感器, 例如重力加速度伟感器、 温度亻考感器等。
伟感器的驱劫程序在Linux中没有柝准, 通常只需要提供埃取和查洵接口即可。 佳感器的
硬件抽象尼是sensors硬件模玦, 需要対多神多介亻耜感器迸行灶理。
伟感器的本地框架昆次包括伟感器的服各部分和用于定又框架的libgu庠
i 。 対于Java
展次, 伟感器通迥JNI提供了Java尼中使用的�. 其中主要是荻取亻考感器敷据和精度変化
的接口, 也提供了部分用于没置接口。
伟感器系统的相美內容如表6-1所示。

表6-1 侍感器系统的相芙內容
Android 的展次 伟感器系统部分 描 述

硬件居次 加速度、 温度等各 'Fi' 伟感器 各介伟感器使用不同的硬件

操作系统扆 可以使用字符没各或者 sys 文件系统等 霨要提供埃取敷据的接口

本地的硬件抽象扆 sensors 硬件模坎 需要対多利\多今亻耜感器迸行灶理

本地框架扆 伟感器服劣、 hbgui 庠 使用了 Binder lPC 机制

Java 框架尼 SensorManager 和対特別亻考感器的姓理 有框架展灶理屏幕方向的颜外灶理


Java 尼的 AP! 同框架屄部分 框架展中的內容基本也是 API

伟感器系统可以対多介亻耜感器迸行灶理, 伟感器的炎型有多秤, 每神炎型也可以有多


介伟感器。 而亻耜感器厲于榆入系统, 諭入的信息比絞简单, 伟感器系统的主要目的是荻得
各介亻刮感器榆入的信息。
第6章 侍感器系统 ooe

6.2 伟感器子系统的結枸

�6.2.1 忌体結杓
Android 2.2 版本之前的佳感器系统比絞简单,由JNI部分直接调用硬件抽象扆米实瑰,
在本地的框架扆中, 没有使用其他的庠。 自 Android 2.3 系统之后, 伟感器系统的結杓迸行
了调整, 自下而上包含了驱劫程序、 伟感器硬件抽象扆、 本地尼次、 伟感器 Java 框架奕、
Java 框架中対伟感器的使用、 Java 皮用尼。 伟感器系统的結杓如囹 6-1 所示。

ApplicationContext OrientalionEventlislener

Sensor
Class
Sensor Manager
巳I Sensor£vent I (SensorEventlistener

Java框架扆

SensorJNI Sensor Service (libsensorservice.so)

本地框架扆

Linux 內核扆 [辶三三-二 招6-1 Android的亻耜感器系统結杓


i

Android 的伟感器系统自下而上, 包括以下几介部分的內容。


Cl)具体平台实現的亻刮感器驱幼程序
(2) 伟感器的硬件抽象扆
佳 感 器 的 硬 件 抽 象 扆为杯准的 sensors 硬 件模坎, 其接口失文 件路往为:
hardware/libhardware/include/hardware/sensors.h, 伟感器的硬件抽象展实現后, 将生成名杯
为 sensors.<hardware>.so 的幼态庠。
(3) 伟感器的本地框架尼
包括伟感器服各和伟感器框架。
ft frameworks/base/services/sensorservice/: 伟感器服各部分代碼。
伟感器服各部分生成劫态庠 libsensorservice.so。
伟感器框架在 libgui 中, 生成劫态庠 libgui.so。
a frameworks/base/include/gui/: libgui�文件。

97�
• O O Android板级支持与硬件相美子系统

e frameworks/base/libs/gui/: libgui源代碼 。
(4) 佳感器系统的JN1
e frameworks/base/core/jni/android_hardware_SensorManager.cpp
(5) 伟感器系统的Java框架
e frameworks/base/include/core/jave/android/hardware/: 其中的伟感器部分。
本目汞中対皮包含了android.hardware, Sensor部分的內容为 Sensor*.java文件。在Java
扆対佳感器JavaAPI可以分成几介部分。 框架尼也包括了调用亻考感器米实現屏幕自幼旋特
功能的部分。
3 提示: Android 4.2 的SensorService 的內容在frameworks/native/的目汞中。
�6.2.2 本地框架展

1. libgui庠相夫的內容
lib舺庠包括亻寺感器本地上下扆几令染文件和源文件 , 提供伟感器的基本突和为本地扆
程序所调用的接口。文件如下所示。
e Sensor.*: 单 一亻耜感器的描述, 包括名稔、 精度、 延迟等。
e SensorManager.*: 侍感器的管理器, 可以荻得 Sensor的列表。
e SensorChannel.*: 侍感器的通道, 提供用于波写的數据接口。
e SensorEventQueue. *: 伟感器的事件臥列, 提供渙/写和対亻耜感器的控制接口 。
其中引用了 android/sensor.h染文件, 包括Asensor、ASensorEvent等癸。 伟感器的結
杓自Android 2.3改为如此, 主要是为了提供 NDK所使用的本地接口。

今提示:Android 2.3以后的佳感器的核心运行于系统服夯器的迸程 , 不再是亙用迸程。

以I玕失的几介文件是基于Binder机制的远程接口 , 需要下扆米实現。
eISensorServer. *: 侍感器的服各器。
e ISensorEventConnection. *: 表示亻考感器事件的连接。
ISensorServer具有两介函數 , 用于荻得亻考感器的列表和侍感器事件的连接:
virtual Vector<Sensor> getSensorList () = 0;
virtua1c, sp-s_IS細sor_Ev鄄幽虫逸 _gtig_n> 逗fite§.e �orJ;又包!1tCOI,H\e,c;.):記Jn l)....;;..._0;

ISensorEventConnection用于控制伟感器 , 其中的函數如下所示:
virtual sp<SensorChannel> getSensorChannel () const = 0;
virtual status_t enableDisable(int handle, bool enabled) = 0;
virtual status_t se_tEventR色 te乜民: han_q).e, nsec竺__!- ns) = O;
根据以上的 调用荻取到SensorChannel之后 , 作为从下展荻取亻耜感器信息的通道。迏两
介远程接口的实現均在本地庠sensorservice中完成。

�98
第6章 侍感器系统 oo•

2. sensorservice

sensorservice 是实現伟感器服各的本地庠, 主要通迥调用亻考感器硬件抽象扆实瑰 libgui

定叉的远程接口, 其客户端則通迥 libgui 的接口在远程対其迸行调用。


SensorService.h 中定 叉 了 sensorservice 中 的核 心內 容 , SensorService 奕 继承了
BnSensorServer , 其中的 SensorEventConnection 癸继承了 BnSensorEventConnection 。

SensorService 奕是 Android 中 一
令典型的 Binder 服各。
Sensor Device 表示亻考感器没各, 即亻耜感器的底屄实現, 也是亻耜感器硬件抽象扆的调用
者。 在其杓造函數中, 打升了亻考感器硬件抽象扆的模玦, 然后调用了其中的亻刮感器列表,
没置延迟, 激活亻耜感器, 等待數据返回 (poll) 等接口。
SensorInterface 癸表示

令抽象的侍感器接口, 由于重力加速度、紱性加速度、旋若咆
亮等亻虯感器的信息需要迸行額外灶理, 因此 GravitySensor 、 LinearAcee Ierati onSensor 、

RotationVectorSensor 等几令奕是其继承者, 在 SensorDevice 中根据伟感器的炎型迸行了不


同的调用姓理。
按照 一 般的 Android 启劫流程, SensorService::instantia圖)函數被 Java 的服各迸程
(SystemServer) 的 JNI 部分 f司接调用, 因此 sensorservice 本身将运行 SystemServer 迸程,

其初始化 般在 SurfaceFlinger 服各之后。

�6.2.3 侍感器系统的JNI
android_ hardware_SensorManager.cpp 是伟感器部分的 JNI 实珗, 主要实現了 Java 的
android.hardware 中的 SensorManager 哭并没置了 Sensor 炎的信息。
主要通迥调用 libgui 庠的 Sensor、 SensorManager 和 SensorEventQueue 几今癸米实珗功
能, 核心的实現是 sensors_data _poll() 函數, 內容如下所示:
static jint sensors_data卫oll(JNIEnv *env, jclass clazz, jint nativeQueue,
j floatArray values, jintArray status, jlongArray timestamp) {
sp<SensorEventQueue> queue(reinterpret_cast<SensorEventQueue *>(nativeQueue));
if (queue == 0) return 一 1;
status_t res;
ASensorEvent event;
res = queue->read(&event, 1); //向下扆涙取數据
II省略部分內容
Jl.nt accuracy= event.vector.status;
env->SetFloatArrayRegion values, 0, 3, event.vector.v); II没置敖值
env->SetintArrayRegion(status, 0, 1, &accuracy);
env->SetLongArrayRegion(timestamp, 0, 1, &event.timestamp);
return event.sensor;

其中的 SensorEventQueue 在 sensors_create_queueO函數被創建, 作为 Java 扆保存的本


地句柄, 在 sensors_data_poIIO 函數中被使用。
SensorManager 的初始化迥程如下所示:
static void nativeClassinit (JNIEnv *_env, jclass _this) {
jclass sensorClass= _env->FindClass("android/hardware/Sensor");
SensorOffsets& sensorOffsets= gSensorOffsets;

99�
•oo Android 板级支持与硬件相美子系统

sensorOffsets.name =
env->GetFieldID (sensorClass, "mName", "Ljava/lang/String; ");
sensorOffsets.vendor =
env->GetFieldID (sensorClass, "mVendor", "Ljava/lang/String; 叮;
//省略部分內容

android.hardware 包的 Sensor 癸具有几令私有成贝, 它亻I'] 在 Java 中没有被賦值, 而是


在此灶被賦值。

�6.2.4 伟感器系统的Java居

1. 核心部分
android.hardware 包中的具有几介伟感器系统的哭都是 Java 扆的 APL 如下所示。
• Sensor: 单一 伟感器的描述性文件。
• SensorManager: 实珗亻耜感器系统核心的管理奕。
• SensorEvent: 表示伟感器系统的事件炎。
• SensorEventListener: 伟感器事件的湍昕者接口。

Sensor 奕用于描述 令具体的佳感器, 其主要的方法如下所示:
public class Sensor {
float getMaximumRange {) ( II荻得伟感器最大的范围)
String getName {) ( //荻得伟感器的名杯}
float get Power {) //荻得佳感器的耗能}
float getResolution {) //荻得伟感器的解析度)
int getType () { //荻得佳感器的哭型}
String getVendor {) II荻得伟感器的Vendor )
int getVersion {) //荻得隹感器的版本)

Sensor 奕中 具有名稔为 TYPE_* 的常量, 表示 Android 中支持的伟感器奐型, 而


TYPE_ALL C-1) 表示所有的佳感器奕型。
SensorManager 癸是 Sensor 整介系统的核心, 几令主要方法如下所示:
public class SensorManager extends IRotationWatcher.Stub {
public Sensor getDefaultSensor (int type) { //荻得默从的伟感器 }
public List<Sensor> getSensorList (int type) { //荻得佳感器列表}
public boolean registerListener (SensorEventListener listener, Sensor sensor,
int rate, Handler handler) { //注冊伟感器的盟昕者}
void unregisterListener (SensorEventListener listener, Sensor sensor)
{ //注銷伟感器的瘟昕者 )

其中 getDefaultSensor() 将根据奕型荻得系统中默从的佳感器, getSensorLi頲)可以荻得


伟感器列表,注意,每 一 秤奕型的亻虯感器可以有若干介,因此迏里返回的是 一 介列表 (List)
的形式。 registerListener()和unregisterListener()使用接口作为亻考感器系统的盟昕者, 迏里使
用 Sensor 作为參敖奕型, 作为每 一 令伟感器单狹没置的事件監昕者。
SensorManager 哭中坯定又了其他的 一 些常 量, 如 GRAVITY_* 为重力常量,
MAGNETIC_FIELD_* 为磁協常量, LIGHT_* 为光亮度(亮度)常量。

, 100
第6章 侍感器系统 ooe

SensorEvent炎表示伟感器事件, 实际上是Sensor炎加上了致值(values)、 精 度
(accuracy)、 时囘戳C timestamp)等內容, 迏介突的几令成贝都是公共(public)炎型。
SensorEventListener接口描述了SensorEvent的監昕者, 內容如下所示:
public interface SensorEventListener {
public void onSensorChanged(SensorEvent event);
public vo這onAccuracyChanged(Sensor sensor, int accuracy);

SensorEv��;Listener接口需要由伟感器系统的调用者米实現,onSensorChanged()在伟感
器敖值改変时被调用, onAccuracyChanged()方法在亻考感器精度変化时被调用。

2. 相失部分


android.view中的OrientationEventListener是一令工具奕, 提供了方向信息。
rientationEventListener中包括了 一 介抽象方法, 如下所示:
abstract public void onOrientationChanged(int orientation);

enable()和disable()方法則奐靑使能和禁止亻耜感器。
OrientationEventListener本身通迥调用SensorManager实現, 在荻取伟感器敖据后, 根
据三令方向的加速度信息(X, Y, Z) 和重力加速度计算了圭前方向的還緝。
Android系统根据方向自幼若幼晁示的功能就是基于OrientationEventListener实現的。

6.3 伟感器BSP的結枸

Android亻考感器系统自伟感器 硬件抽象尼接口以下的部分是非杯准的,因此亻耜感器系统
移植包括亻耜感器的驱劫程序和硬件抽象展。
Android中Sensor的驱劫程序是非杯准的, 只是为了淌足硬件抽象扆的需要。

�6.3.1 驱劫程序
从 Linux操作系统中驱劫程序的角度,Sensor 的驱劫程序没有杯准架杓。 因此在
Android中枸建的Sensor驱劫程序也是通迥 非杯准的 Linux驱劫程序实現的。
Sensor 驱劫程序的主要的职靑是从硬件中荻得亻考感器信息, 通迥在用户空阅调用接
口将敖据伟送給上扆。
Sensor驱劫程序可以基于如下的接口來实瑰。
e Event没各
e Misc朵項字符没各
.直接实現 一 介字符没各的主没各
0使用 sys文件系统
伟感器需要实現与硬件相美的机制包括渙取信息 、 阻塞、 控制。 送三者対皮的經典接
口分別是 read、 poll和ioctl, 事实上只有渡取功能是必須实現的。
由于侍感器本身是一 神荻取信息的工具, 因此将其实瑰 为用于榆入的 Event 没各是很

101�
•oo Android板级支持与硬件相美子系统

自然的方式。Event没各可以实現用于阻塞的 poll调用, 在中新到米时将poll解除阻塞,


然后实現 read调用, 将數据亻考逸給用户空向。 如果使用Event没各, 噩然可以使用Input
驱幼框架中 定叉的敖据哭型。
如果使用Misc朵項字符没各或者字符没各的主没各实現亻考感器的驱劫程序,实际上和
Event实現的驱劫程序 是很癸似的。 也可以直接实現file_operations中的read、poll和ioctl
接口米实瑰対皮的功能 。浩然,read涙取信息的功能和poll实瑰阻塞的功能也可以 通迥ioctl
米实現 。

�}提示: 如果使用ioctl实現功能, 为了保证用户空阅调用的炅活性, 瓶霎和塽取信息


最好不要在一今命令中实珗。

使用 sys文件系统可以实現 基本的渡、 写功能,対皮驱劫中的show和store 接口实瑰 。


晁然, sys文件系统也可以实現阻塞, 只是通常不迏祥做。

�6.3.2 硬件抽象展的內容

1. Sensor硬件抽象展的接口
sensors.h 是Android亻考感器系统 硬件屄的接口,迏是一 介杯准的Android 硬件模坎之 一 。
失文件中的各令SENSOR_TYPE_*常量表示 各秤伟感器的奕型。
S ensor模坎sensors_module_t的定又如下所示:
struct sensors module t {
struct hw module t common;
int (*get_sensors_list) (struct sensors_module_t* module, struct sensor_t const** list);
);

在杯准的硬件模玦(hw_module_t)的基絀上增加了get_sensors_listO函敖, 用于荻得
伟感器列表。
sensor_t結杓用于描述伟感器, 如下所示:
struct sensor t {
const char* name; II侍感器的名稔
const char* vendor; II伟感器的Vendor
int version; / I伟感器的版本
int handle; II伟感器的句柄
int type; / I伟感器的哭型
float maxRange; II伟感器的最大范围
float resolution; II侍感器的解析度
float power; II伟感器的耗能(估计值,单位为mA)
void* reserved[9];

sensor_t表示单 一 亻考感器的信息, 与Android Java尼的伟感器 奕対皮。


sensors_vec_t結杓表示的是一令亻考感器 敖据向量的結杓体, 內容如下所示:
typedef struct {
union {
float v[3]; II使用3介浮燕致据表示
struct (float x; float y; float z; J; II使用紬坐步示表示

�102
第6章 侍感器系统 ooe
struct { float azimuth; float pitch; float roll; } ; //使用极坐杯表不
};
int8_t status; II狀态信息

--
uint8_t reserved[3];
) sensors
' "" ,'vec"'t ,·


按照如上的定叉,sensors_vec_t數据結杓的大小为16字节,其中第1介成昃是 令共
用体,可以表示为3介单精度浮燕敖, 或者紬坐杯和极坐杯的单精度浮燕敖的格式。 最后
的3令字节朴足了迏令結杓体为16字节。
sensors_event_t數据結杓表示亻夸感器事件的敖据, 如下所示:
typedef struct !
int sensor; // sensor杯沢符
int32_t type; II哭型
int32 t reservedO; II保留
int64_t timestamp; II时伺戳(单位: nanosecond)
union {
sensors vec t vector; // x,y,z矢量
sensors vec t orientation; II方向(单位: 度)
sensors vec t acceleration; II加速度(单位: m/s勺
sensors vec t magnetic; II磁矢量(单位: uT)
float temperature; II温度(单位: 掇氏度)
float · distance; II距萬(单位: 米)
float light; II光度(单位: SI luminous flux)
float pressure; II圧力(单位: hPa)
);
uint32 t reservedl[4]; II保留
} s�rg;_o五1>_�vent一店

在sensors_event_t中, 使用 令共用体表示不同的佳感器各自的敖据突型, Sensor則
是具体伟感器的杯沢。
sensors _poll_device_ t表示亻考感器的没各, 其定又如下所示:
struct sensors_poll_device_t {
struct hw device t common;
int (*activate) (struct sensors_poll_device_t *dev, 刀激活
int handle, int enabled);
int (*setDelay) (struct sensors_poll_device_t *dev, II没置延迟
int handle, int64_t ns);
int (*poll) (struct sensors_poll_device_t *dev, II荻取敖据
sensors_event_t* data, int count);
);

activate、 set_delay厲于輔助功能, poll則提供了荻取敖据的功能, 此调用将阻塞, 直


到有敖据返回。

2. 实瑰和调试Sensor硬件抽象展

Sensor硬件抽象扆主要是sensors_poLI_device_t中的几介函數指針。
poll函數指針是亻夸感器的核心功能, 其名杯就是"poll"的原本含叉,调用时被阻塞,
" "
直到亻耜感器荻得敖据时返回。poll调用的实現在經典的狀态中是 阻塞十淩取 迏两介坏节,
都可以通近调用驱幼程序的相哭接口(可能是非杯准的 ioctl)米完成。 由于侍感器没各通

常耗費系统瓷源不佘很多,因此阻塞可能不需要 定实現,取而代之的是使用固定的延迟。

103�
• O O Android板级支持与硬件相美子系统

activate函數指針用于激活—元效; setDelay函數指針用于没置延时, 也 就是没置了 亻夸


感器的精度。

在 Android 系统中, 支持多神奕型的伟感器, 同 神奕型的佳感器也 可以支持多介,
因此在硬件抽象扆中也需要灶理迏介內容。 首先需要杓建 一 介 sensor_t 奕型的數組表示各
今伟感器, 在阻塞方面, 如果驱劫程序中实現了杯准的poll接口, 在用户空向的调用中可
以通迥调用 select 实現多路造掙的功能。 各令亻考感器的驱劫可能没有杯准的实現, 甚至可

能接口都是不 致的, 迏时就需要使用韐循的方式米灶理了 , 必要时可以使用多鈛程。
nusensors是用于亻耜感器的潿试工程,代碼路往为: hardware/libhardware/tests/nusensors/ 。
由C曰- 的代碣組成, 其中的內容将生成test-nusensors 可执行程序。 迏部分程序直接打玕 硬
件抽象扆的模玦迸行调用, 其代碼与亻考感器JNI部分癸似。 但是可以在命令行直接运行,
不需要其他命令行參數,它将直接调用sensors_open 打升几介亻考感器并使能,然后调用poll
从伟感器中取出數据, 从命令行打印出結果。

6.4 伟感器BSP的实現

伟感器的BSP实現由非柝准的驱劫程序和硬件抽象扆組成。仿真器的实瑰使用特定的
榆入模姒伟感器的諭入, 而Nexus One 和Nexus S 的伟感器硬件抽象扆則使用了奕似的实
瑰結杓。

�6.4.1 仿真器的实瑰
Android仿真坏境中,亻虯感器的实現方式是通迥一 介硬件抽象扆涙取文件系统中的文件
米荻取伟感器信息。 迏祥, 就 不需要驱劫程序, 只需要实現一 令亻耜感器的硬件抽象扆。
Android为仿真器提供了 一 介伟感器硬件抽象尼的示例实瑰。
仿真器Sensor 硬件抽象尼代碼的路往为: sdk/emulator/sensors/
迏里包含了 一 介Android.rnk文件和 一 介源文件sensors_qemu.c, 經迥編诵将形成一 介
单狼的模玦, 即劫态庠sensors.goldfish.so (中阅的goldfish表示严品名)。 它将被放置在目
杯文件系统的system/lib/hw/ 目汞中。
此姓的实現夏用了Android 2.3以前的亻耜感器系统的实瑰使用敖据没各和控制没各的模
式 , 并迸行了封裝。
sensors_ qemu.c表示模玦的打玕函數如下所示:
static int open_sensors(const struct hw_rnodule_t* module,
const char* name, struct hw_device_t* *device) {
int status = -EINVAL;
if (!strcrnp(narne, SENSORS_HARDWARE_POLL)) {
SensorPoll *dev = rnalloc(sizeof(*dev));
rnernset (dev, 0, sizeof(*dev));
dev->device.common.tag = HARDWARE DEVICE TAG;
dev->device.common.version = O;
dev->device.common.rnodule = (struct hw_rnodule_t*) module;
dev->device.common.close = poll close;
dev->device._poll = poll_poll;

�104
第6章 侍感器系统 ooe
dev->dev1.ce.activate = poll activate;
dev->device.setDelay = poll_setDelay;
dev->events fd = -1;
dev->fd = -1;
*device = &dev->device.common;
status = 0;

return status;

sensors_qemu.c 表示模玦的核心結杓定又如下所示:

static struct hw_module_methods_t sensors_module_methods = {


.open = open_sensors
};
struct sensors module t HAL MODULE INFO SYM = {
.common = {
.tag = HARDWARE MODULE TAG,
.vers1.on maJor = 1, II主版本弓和次版本弓
.vers1.on m1.nor = 0,
.id = SENSORS HARDWARE MODULE ID, II伟感器硬件模坎
.name = "Goldfish SENSORS Module",
.author = "The Android Open Source Project",
.methods = &sensors_module_methods,
},
.get_sensors_list = sensors_get_sensors_list II返回伟感器列表的函敷
-
.
.
'
.

其中 sensors_get_sensors_ Ii 噩)函敖返回伟感器列表。 sensor_t 奕型的 sSensorListlnit 數


組共定叉了 5 令亻考感器: 加速度、 磁汤、 方向、 温度和接近。
poll_poll() 函敖就是 sensors_poll_device_t 結杓中 poll 的实瑰,调用了 data_poll ()函數
完成功能, 其中实現的核心部分如下所示:
static int data_poll(struct sensors data device t *dev, sensors data t* values) {
SensorData* data = (void*)dev;
while (1) {
char buff[256];
int len = qernud_channel_recv(data->events_fd, buff, sizeof buff-1);
float params [3];
int64 t event time;
buff[len) = 0; //涙取伟感器信息
II如果字符串的數值是"wake"則表示喚醒
if (! strcmp((const char*)data, "wake")) { return Ox7FFFFFFF; }
II "acceleration:<x>:<y>:<z>" corresponds to an acceleration event
if (sscanf(buff, "accelera户on:兔g:令g:%g",
params+O, pararns+l, pararns+2) == 3) {
new_sensors I = SENSORS_ACCELERATION;
data->sensors[ID_ACCELERATION].acceleration.x = params[O];
data->sensors[ID_ACCELERATIONJ.acceleration.y = pararns[l);
data->sensors[ID_ACCELERATION].acceleration.z = params[2];
continue;

II省略方向、 磁汤、 温度、 接近伟感器的姓理
II方向伟感器"orientation:<azirnuth>:<pitch>:<roll>"
II "magnetic:<x>:<y>:<z>"
II "temperature:<celsius>"
II "proximity:<value>"
江(sscanf(buff, "sync: 叩lld", &event_tirne) == 1) {

105�
•oo Android板级支持与硬件相美子系统

if (new sensors ){
data->pendingSensors = new_sensors;


int64_t t = event_time * lOOOLL; //特換成nano-seconds单位
江(data->timeStart == 0) { / /首次同步的时候做特殊灶理
data->timeStart = data now ns();
data->timeOffset = data->timeStart - t;

t + = data->timeOffset;
while (new_sensors) {
uint32 t i = 31 - builtin clz(new sensors);
new sensors &= -(l<<i);
data->sensors[i] .time = t; //荻得时冏信息

return pick sensor(data, values);


) else (//省略措课灶理的內容 }
continue;

灶理的流程是: 分突型塽取伟感器敖据, 給対皮的數据結杓結杓賦值, 由于本例是軟


件仿真示例, 因此其取出信息的內容來自軟件的Buffer, 没置結果通迥没置 sensors_event_t
敷据結杓米体現。

- 提示: 不同Android版本的仿真器硬件抽象扆实現的佳感器介教有差別。

侍感器敖据的Buffer來自于qemud_channel_recv荻取的信息。 qemud_channel_recv可
以和qemud_channel_send使用/dev/socket目泵中的套接字qemud米实瑰通信。

�6.4.2 Nexus One系统实瑰


Nexus One系统具有以下几介伟感器硬件。
a BMA150: 三介 方向的加速度(Accelerometer)伟感器。
a AK.8973: 三令方向的磁汤(Magnetic field)伟感器和方向(Orientation)伟感器。
• CM3602: 接近(Proximity)伟感器和光亮度(Light)伟感器。

1. 驱劫程序

Nexus One系统实珗伟感器的没各驱劫程序由MISC字符没各和 input没各杓成, 有以


下几今没各节燕。
a /dev/bmal50: BMA150的MISC没各节熹。
a /dev/akm8973_aot和/dev/akm8973_daemon: AK.8973的MISC没各节燕。
• /dev/cm3602: CM3602接近亻考感器的MISC没各节燕。
a /dev/lightsensor: CM3602 光亮度亻考感器的MISC没各节燕。
a /dev/inputJevent6: 名为compass, 磁汤和方向亻考感器的Event没各节煮。
a /dev/input/event2: 名为proximity, 接近亻考感器的Event没各节燕。
a /dev/inputJevent7: 名为Lightsensor-level, 光亮度伟感器的Event没各节煮。
Linux 內核中的源代碼涉及以下几令部分。

, 106
第6章侍感器系统 ooe

a drivers/misc/akm8973.c: AK8973 (compass )的MISC和 Event 驱劫,l2C的惡紱


没各。
e drivers/input/misc/capella_cm3602.c: CM3602的M ISC字符驱幼和proximity的Input
驱劫。
e arch/arm/mach-msm/board-mahimahi-microp.c: lightsensor 的 MISC 的没各节燕和
lightsensor-level的Event没各节煮,迏是l2C没各。

2. 硬件抽象展部分

Nexus One系统伟感器的硬件抽象尼包含了5介伟感器,B MA150 (AK.8973)加速度、


AK.8973磁汤、AK8973方向、CM3602接近和CM3602光亮度。硬件抽象扆中包含的代碼
路往为: device/htc/passion-common/libsensors/, 包括以下文件。
a sensors.c: C语言的主入口文件。
e nusensors.cpp: 伟感器的硬件抽象扆的维杓 实現。
a SensorBase主伟感器 实瑰的基癸。
a lnputEventReader. *: 通迥Input驱劫涙取事件的工具。
e AkmSensor. *: 操作/dev/akm8973
_aot没各,名稔为"compass", 磁汤和方向亻考感器的
的实現。
a ProximitySensor. *: 操作/dev/crn3602没各,名稔为"proximity", 接近亻耜感器的实瑰。
a LightSensor.*: 操作/dev/lightsensor 没各,名稔为"lightsensor-level", 光亮度伟感器
的实瑰。
nusensors c
. pp 結杓的內容如下所示:
enum { II表示不同的伟感器的枚举值,內部使用
light = o,
proximity = 1
akm = 2,
numSensorDrivers,
numFds,
];
struct pollfd mPollFds[numFds];
SensorBase* mSensors[numSensorDrivers];
sensors_poll一_context_t:: sensors_poll_context_t () (
mSensors [light] = new LightSensor (); //建立侍感器的真正实現,并賦值
mPollFds [light].fd = mSensors[light]->getFd (); //伟感器使用的文件描述符
mPollFds[light].events = POLLIN;
mPollFds[light].revents = O;
II省略部分內容


nusensors的內容实际上是 介伟感器的代理实珗。它实現了亻耜感器的硬件抽象尼所需
要的結枸,其中调用的LightSensor等內容則是伟感器的真正实現。此姓每秤亻耜感器的实現
作为袖立模玦,都继承自SensorBase癸。

�6.4.3 Nexus S系统实瑰


Nexus S系统具有以下几介亻耜感器 硬件。

107�
•oO Android 板级支持与硬件相美子系统

a KR3DM: 三令方向的加速度亻耜感器。
• AK.8973: 三介方向的磁汤伟感器和方向伟感器。
a GP2A: 接近和光亮度亻考感器。
• K.3G: 螺旋仅 (Gyroscope) 伟感器。

1. 驱劫程序
Nexus S 系统亻耜感器有以下几介没各节燕。
• /dev/accelerometer: KR3DM 的 MISC 没各节煮。
• /dev/akm8973: AK.8973 的 MISC 没各节燕。
• /dev/input/event6: 名为 compass, 磁汤和方向亻耜感器的 Event 没各节煮。
• /dev/input/event3: 名为 proximity, 接近亻考感器的 Event 没各节魚。
• /dev/input/event4: 名为 ligbtsensor-level, 光亮度亻考感器的 Event 没各节燕。
• /dev/input/event l : 名为 gyro, 螺旋亻又亻考感器的 Event 没各节熹。
Linux 內核中的源代碼涉及以下部分。
• drivers/misc/kr3drn.c: 实現了 KR3DM 的 MISC 没各节盅 l2C 惡綫的地址为 1-0009 。
• drivers/misc/ak8973.c: 实瑰了 AK.8973 (compass) 的 MISC 和 Event 驱劫, I2C 紱的
地址为 1-00lc。
• drivers/input/misc/gp2a.c: 实瑰了 GP2A 的 lightsensor-level 和 proximity 部分的 Input
驱劫, gpio 控制的没各, l2C 惡紱的地址为 11-0044。
• drivers/input/misc/k3g.c: 实現了 K3G 的 GP2A 部分的 Input 驱劫, l2C 惡綫的地址为
0-0069。

2. 硬件抽象展部分
Nexus S 系统伟感器的硬件抽象尼包含了 6 令亻耜感器: KR3DM (AK.8973) 加速度、
AK.8973 磁汤、 AK.8973 方向、 GP2A 接近、 GP2A 光亮度和 K.3 G 螺旋仅。 硬件抽象展包含
代碼路往为: samsung/crespo/libsensors/, 包括以下文件。
• sensors.*: C 语言的主入口文件。
• nusensors.cpp: 伟感器的硬件抽象扆的結杓实現。
• SensorBase.*: 伟感器实瑰的基癸。
• InputEventReader.*: 通迥 Input 驱劫涙取事件的工具。
• AkmSensor.*: AK.8973 磁汤和方向亻耜感器的实瑰, 调用 liba际l.SO 。
• ProximitySensor. *: GP2A 接近亻鉭感器的实現。
• LightSensor.*: GP2A 光亮度亻考感器的实珗。
• GypoSensor.*: K3G 螺旋仅伟感器的实珗。

提示: Nexus One 和 Nexus S 的佳感器实現結杓, 已綬成为典型和通用的方法。

�108
第6章侍感器系统 ooe

�6.4.4 Galaxy Nexus系统实瑰


Galaxy Nexus系统包括了焱多的佳感器,迏些亻耜感器都是OMAP灶理器外板级 硬件,
使用I2C 的方式与OMAP 相连。 包括了 Sharp 的 GP2A 的接近和光綫伟感器, Bosch 的
BMP180氕圧计,以及InvenSense的若干介运劫相美的亻考感器。

1. 驱劫程序

Galaxy Nexus的伟感器都是连接于I2C恙紱的, 內核中板级定叉的侍感器相失的文件


为: arch/arm/mach-omap2/board-tuna-sensors.c。 其中 的 i2c_board_info癸型的結杓
tuna_sensors一i2c4_ boardinfo中定又了5介 I2C的没各,如下所示:
static struct i2c board info initdata tuna_sensors_i2c4_boardinfo[] = {
I2C BOARD INFO("mpu3050", Ox68),
.irq = OMAP_GPIO_IRQ(GPIO_GYRO_INT), .platforrn_data = &mpu_data,),
I2C_BOARD_INFO("bma250", Ox18),
.irq = OMAP_GPIO_IRQ(GPIO一ACC_INT), .platform_data = &mpu_data .accel,},
I2C_BOARD_INFO("yas530", Ox2e),.irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
.platform_data = &mpu_data.c6mpass,) ,
I2C BOARD INFO("gp2a", Ox44), .platform_data = &gp2a_pdata, },
I2C BOARD INFO("bmpl80", Ox77),),

伟感器的硬件 连接方式都是 12C, 因此各自有不同的地址,但是它亻[]在用户空冏有的


是input 没各,有的是MISC字符没各。
几介亻考感器相美的 input 没各节燕如下所示。
e /dev/input/event3: Sharp的GP2A的接近 伟感器("pro汕nity")。
e /dev/input/event4: Sharp的GP2A的 光紱 伟感器("lightsensor-level")。
e /dev/input/eventO: Bosch的BMP180勺圧计(" barometer ")。
迏几介內容都是12C的没各, 在运行时sys 文件系统 的/sys/bus/i2c/devices/目汞中, 它
亻[]分別为12C的第4介控制器上面的 0044和0077两介地址的没各,如下所示:
shell@android:/ $ cat /sys/bus/i2c/devices/4-0044/name
gp2a
shell@android:/ $ cat /sys/bus/i2c/devices/4-0077/name
坪pl

迏几介伟感器的实現,都是通這l2C惡紱注冊了input的没各节魚,其代碼路往为
如下。
e drivers/input/misc/gp2a.c: GP2A 接近和光紱亻耜感器的驱劫。
e drivers/misc/bmp l 80.c: BMP180的驱劫。

加速度伟感器由InvenSense的各介伟感器提供, MPU3050 是 介三紬伟感器。其代碼
路往为: drivers/misc/inv_mpu/, 包括mpu-dev.c、mldl_cfg.c、mlsl-kemel.c等几令源文件。
accel 目汞中的提供了bm250的加速度亻耜感器的实現,其l2C的地址为4-0018; compass中
提供了yas530磁力亻耜感器, 其l2C的地址为4-0028。
以上的实現在/dev/中提供了几令MISC的没各节魚,包括mpu 、 accelirq 、 compassirq 、
mpuirq和timerirq。

109�
• O O Android 板级支持与硬件相美子系统

2. 硬件抽象展
Galaxy Nexus 的系统伟感器部分的硬件抽象扆使用 Tuna 板的通用实瑰, 代碼路往为:
device/samsung/tuna/libsensors/, 生成 sensors.tuna.so 包括以下几今文件。
e sensors.*: 伟感器硬件抽象扆的主入口文件。
e SumsungSensorBase. *: 三星亻耜感器实瑰的基炎。
e InputEventReader. *: 通迥 Input 驱劫懷取事件的工具。
e LightSensor. *: 名稔为 "lightsensor-level", 光亮度伟感器的实現。
e ProximitySensor. *: 名秣为" proximity", 接近侍感器的实瑰。
11
e PressureSensor. *: 名稔为 barometer 11, 勺圧计伟感器的实瑰。
其代碼結杓是典型的亻考感器硬件抽象屄的結杓。 SumsungSensorBase.*根据 sys 文件系
统的路往 /sys/class/input/渙取各秤没各信息。迏里源代碼中 LOCAL_SENSORS 宏的值为 3,
此姓的实瑰只負靑了驱劫中 3 介 input 没各対皮的 3 今亻耜感器。
InvenSense 的几介伟感器的內容在二迸制的庠 libinvensense_ hat.so 中实瑰, 并被迏里
调用。 硬件抽象展 sensors .cpp 的 sensors卫oll_context_t() 函數调用了 MPLSensor 的內容,
代碼的片段如下所示:
sensors_poll_ context_t: : sensors_poll_context_t() {
FUNC_LOG;
MPLSensor* p_mplsen = new MPLSensor(); //调用libinvensense_hal .so庠中的內容
setCallbackObject(p_mplsen); //建立回调函數灶理MPLSensor的事件
numSensors =
LOCAL一SENSORS + p_mplsen->populateSensorList(sSensorList + LOCAL一SENSORS,
sizeof(sSensorList[O]) * (ARRAY_SIZE(sSensorList) - LOCAL_SENSORS));
mSensors[mpl] = p_mplsen;
mPollFds[mpl] .fd = mSensors[mpl]->getFd();
mPollFds[mpl] .events = FOLLIN;
mPollFds[mpl] .revents = O;
mSensors[mpl_accel] = mSensors[mpl];
mPollFds[mpl_accel] .fd = ((MPLSensor*)mSensors[mpl]) ->getAccelFd();
mPollFds[mpl_accel] .events = FOLLIN;
mPollFds[mpl_accel] .revents = 0;
mSensors[mpl一戶mer] = mSensors[mpl];
mPollFds [mpl_timer] .fd = ((MPLSensor*)mSensors [mpl]) ->getTimerFd();
mPollFds[mpl_timer] .events = FOLLIN;
mPollFds[mpl timer] .revents = 0;
// 省略部分內容

在 InvenSense 的几介亻考感器的外理流程中, 需要首先根据 libinvensense_hal.so 庠提供


接口得到各令亻刮感器的文件描述符 fd, 将它亻i'J 保存, 之后的灶理流程就和其他亻考感器基本
相同了。

�110
第7章
音頻系统

7.1 音頻系统概述

Android的音颜系统提供音颜系统対音颜硬件的没各迸行操作,其主要功能是音颜敷据
的榆入/諭出和控制功能。
音頻系统対皮的硬件包括耳机、 拐声器、 変克风等。 灶理器中的音颜控制器是軟件系
统直接操作的內容。 Linux中音颜没各有杯准的驱劫框架,Android也可以使用 其他癸型的
驱劫程序。 音颜的硬件抽象扆使用 C+十奕继承的方式, 包括音颜没各的諭入/諭出和控制,
也包括音颜没各的策略管理部分。
音颜系统的本地部分有me山a庠中定叉的框架, 由AudioFlinger实瑰, 它提供的本地
扆接口提供本地的媒体播放器等程序迸行调用。 音颜系统也通迥JN1向Java扆次提供相皮
的接口, Java扆音頻接口基本上対本地屋接口的封裝。
音颜系统的相矢內容如表 7-1 所示。

表 7-1 音頻系统的相夫內容
Android的屋次 音頻系统部分 描 述

硬件展 耳机、 拐声器、 安克风等硬件和音颜控制器 榆入型和榆出型的硬件比絞突似

驱劫系统展 ALSA、 oss 或者其他音颜没各驱劫 一般要通近若干介字符没各节燕

本地的硬件抽象扆 C丑展次的Audio和Audio

Policy接口 主要操作部分和策略部分可单袖实瑰
本地框架昆 AudioFlinger、 media中的音颜部分 使用了Bmder !PC机制 , 也具有本地接口
Java框架尼 AudioTrack、 AudioRecorder、 AudioSystem等 本地展的封裝 , 可迸行數据流和控制操作
Java孱的AP! 基本同框架毘部分

音颜系统的尼次絞多, 但各介尼次都是三介方面的內容: 音颜敷据流榆入、 音颜數据


流諭出和音颜的控制。音颜敖据流主要是PCM格式的音颜原始數据,控制方面則包括通道
的迭拌。Java扆通常只是迸行控制方面的操作, 敖据流的操作大都通迥本地接口迸行。
• O o Android 板级支持与硬件相美子系统

7.2 音頻子系统結杓

�7.2.1 恙体結枸
Android 音颜系统包括驱劫程序扆、 硬件抽象扆、 Audio Flinger 、 本地框架庠、 Java 框
架突和 Java 皮用尼対 Audio 系统的调用。
Audio 系统的結杓如囷 7-1 所示。

Java座用展

Java Audio Class


Java框架昆

Audio
本地API

Audio Flinger (libaudioflinger .so)

·--··
本地框架昆
一一
Linux內核扆
移植
··-··
部分

囹7-1 Android的Audio音颜系统結杓

自下而上, Android 的 Au山o 系统分成以下几介部分。


(I) 驱劫程序
Linux 的音颜系统可以使用 oss 、 ALSA 等杯准的架杓或者其他自定又驱幼,各秤驱劫
实現的內容癸似, 提供対 Audio 没各的控制通道和數据通道。
(2) 硬件抽象尼
Audio 硬件抽象扆接口是 C丑形式的, 需要实瑰者继承并完成功能, 其代碼路往为
hardware/libhardware_legacy/include/hardware/, 包括以下的文件。
• AudioHardwarelnterface.h: Audio 主要的硬件抽象扆接口。
• AudioPolicylnterface.h: Audio 策略扆的接口。
(3) 本地框架尼: Audio Flinger 和 Audio 框架部分
• frameworks/base/include/media/: Audio 框架部分失文件。
• frameworks/base/media/libmedia/: Audio 框架部分源代碼。

Audio 本地框架是 media 庠的 部分, 本部分內容被編诵成庠 libmedia.so。 定又 Audio
部分的接口, 迏些內容实际上也作为 Audio 系统対本地展的接口。

, 112
第7 章 音颜系统 ooe

a frameworks/base/services/audioflinger/: Audio Flinger 的实現。


Audio Flinger 实际上是対 Audio 框架部分的实瑰, 作为 Audio 系统的本地服各部分使
用, 被編诵成庠 libaudioflinger.so。
(4) JNI 部分: Audio 本地尼接口的直接封裝
a frameworks/base/core/jni/: 和 Audio 相失的几介文件。
框架扆的 JNI 庠 libandroid_runtime.so, Audio 的 JNI 是其中的一部分。
(5) Audio的Java 部分
a frameworks/base/media/java/android/media/: 和 Audio 相美的几介炎。
android.media 包中具有和 Audio 相失的部分, 主要包含 Audio 系统中的几介突和更上
扆的 AudioManager。

le 彞Android4.x 的音颜系統升始使用硬件模決作为硬件抽象扆, 与上迏結枸不同。 I


�7 .2.2 Audio的本地框架展

1. media 庠中的 Audio 框架部分



Android 的 Audio 系统的核心框架在 media 庠的 部分。 其中的失文件分成两介部分,

介部分是提供給上尼调用的接口, 另 一 介部分是需要由下扆实瑰的接口。
提供給上尼调用的接口包括以下几介癸。
a AudioSystem.h: 音颜管理的 AudioSystem 癸, 其中也包括哭型的定叉。
a AudioTrack.h: 放音癸 AudioTrack, 包括控制函致和數据函敖 write()。
a AudioRecord.h: 呆音奕 AudioRecord, 包括控制函數和數据函敷 read() 。
a AudioEffect.h: 音颜效果癸 AudioEffect, 包括查洵和没置函數。
迏些接口既提供給 JNI 封裝給 Java 扆, 也給本地的其他部分调用。 实际上是 Audio 系
统対本地提供的功能, 相沮于本地 API。 AudioTrack 和 AudioRecord 两介奕功能対皮, 方
向相反, 用于音颜敖据流亻賴諭的炎。 音颜播放器和音颜汞制器利用迏些接口完成本地功能
的调用。
需要由下扆实現的接口也就是音颜系统基于 Binder IPC 的接口, 包括以下几介部分。
a IAudioF血ger.h: 需要下屄实現的音頻惡管接口。
a IAudioTrack.h: 需要下尼实現的放音部分。
a IAudioRecorder.h: 需要下扆实瑰的呆音部分。
a IAudioPolicyService.h: 需要下尼实瑰的音颜策略服各。
迏些接口的实現者就是 Audio 部分的服各 AudioFlingerClient。
Audio System 奕中包含了音颜部分 一 系列的枚举癸型, 迏些奕型在 Audio 系统的各介
扆次中均被使用。 主要的一 些內容如表 7-2 所示。

表 7-2 AudioSystem 中定乂的哭型及其取值


哭型名滁 取值的示例 描述和用途
stream_type SYSTEM、 RING、 ALARM 用于榆出的音颜敷据流來源的突型

113�
•oo Android 板级支持与硬件相美子系统

緤表
哭型名滁 取值的示例 描述和用途
audio_ format PCM 、 MP3 、 AMR_NB 、 A MR_WB 等 音颜流的格式
audio_channels CHANNEL_OUT_FRONT_LEFT 等 音颜通道 , 対皮于 androidmedia.Aud10Format
DEVICE OUT EA腳IECE 、 表示系统中各利各祥的音颜没各 , 如耳机 、
aud1o_devices
DEV ICE_OUT_SPEAKER 等 话筒 、 藍牙等 , 用于音颜策略
device_connection_state DEVICE_STATE_AVAILABLE 等 没各的连接狀态 , 用于音颜策略
output_flags OUTPUT_FLAG_IND!RECT 等 直接或者何接的愉出没笠 , 用于音颜策略
force_use FOR_COMM呣CATTON 、 FOR_MEDIA 等 音颜流的用途的強制没置 , 用于音颜策略
io _config_event OUTPUT_OPENED 、 OUTPUT_CLOSED 等 1/0 変化时刻的事件 , 用于音颜策略

2. AudioFlinger 劫态庠
AudioFlinger 是音颜部分服各的实瑰,它实現了 media 庠中 Audio 尼的几令通這 Binder
IPC 定又的接口,并且调用 Audio 和 Audio 策略的硬件抽象尼米实現。 AudioFlinger 运行于
mediaserver 守护迸程中,在执行的這程中将建立几令綫程奐責各介部分的姓理。
AudioFlinger 中几令核心的实瑰部分包括以下的几介文件。
• AudioFlinger. *: 主要的文件实現上尼 IAudioFlinger 接口。
• AudioPolicyService. *: IAudioPolicyService 的实現。
• AudioMixer. *: 音颜混合工具的实珗。
• AudioResampler. *: 音颜重取祥的实瑰(紱性、正弦 Sine 、三次 Cubic)。
• AudioPolicyManagerBase.h: 音颜策略基癸的定又。
AudioFlinger 主要的還緝就是音颜混合的灶理,送里使用了 AudioMixer 作为实現。
AudioFlinger 完成初始化之后,将为放音、呆音和混音等建立各自的紱程,在紱程循坏中迸
行灶理。
AudioFlinger 另外 一 令职靑是対硬件抽象屄的调用,其 Android.mk 中具有如下定又:
ifeq ($(strip $(BOARD USES GENERIC AUDIO)),true)
LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
LOCAL CFLAGS += -DGENERIC AUDIO
else
LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
Endif
LOCAL_MODULE:_: liba四J.oJli!_lger

涪生成 AudioFlinger 庠时,如果配置宏 BOARD_USES_GENERJC_AUDIO 为 true, 镱


接 libaudiointerface.a 和 libaudiopolicybase.a 靜态庠, 它亻fJ是音頻和音颜策略硬件抽象尼的
默从实現;如果为 false, 镱接 libaudiointerface.so 和 libaudiopolicy.so 劫态庠,它亻j'J是音颜
和音颜策略硬件抽象扆的真正实現。

�7.2.3 Audio系统的JNI和Java展

1. JNI部分
Android 的 Audio 部分通迥 JNI 向 Java 尼提供接口,在 Java 扆可以通迥 JNI 接口完成

�114
第7章 音頻系统 ooe

Audio 系统的大部分操作。
Audio 部分的几令JNI文件如下。
• android_media_AudioSystem.cpp: 惡体控制, 主要调用本地的 AudioSystem。
• android_media_A呻 oTrack.cpp: 諭出坏节, 主要调用本地的 AudioTrack。
• android_media_AudioRecord.cpp: 榆入坏节, 主要调用本地的 AudioRecord。
它們实現 了対 Java 框架居中的 android.media 包中 AudioSystem 、 AudioTrack 和
AudioRecord 三令奕的本地支持。 在 Java 扆, 可以対 Audio 系统迸行控制和啟据流操作,
対于控制操作, 和底尼的姓理基本一 致;対于敖据流操作, 由于 Java 不支持指針, 因此接
口被封裝成了另外的形式。
例如, 対于音颜諭出, android_media_AudioTrack.cpp 提供的是写字节和写短整型的接
口奕型, 它亻i'J都调用 writeToTrac嶽)函數实現, 如下所示:
jint writeToTrack{AudioTrack* pTrack, jint audioFormat, jbyte* data,
jint offsetinBytes, j int sizeinBytes) {
ssize_t written = 0;
if (pTrack->sharedBuffer() == 0) {
written = pTrack->write(data + offsetinBytes, sizeinBytes); //写操作
I else {
if {audioFormat == javaAudioTrackFields.PCM16) {
; Lf((size_t)sizeinBytes > pTrack->sharedBuffer()->size()) {
sizeinBytes = pTrack->sharedBuffer()->size(); //荻取敖据的大小

memcpy(pTrack->sharedBuffer()->pointer(), II內存夏制
data + offsetinBytes, sizeinBytes);
written = sizeinBytes;
} else if (audioFormat == javaAudioTrackFields.PCM8) { // PCM8格式灶理
if (((size_t)sizeinBytes) *2 > pTrack->sharedBuffer()->size()) (
sizeinBytes = pTrack->sharedBuffer() ->size() / 2; //准各护展成16位

int count = sizeinBytes;


intl 6_t *dst = (intl 6 _t *)pTrack->sharedBuffer ()->pointer();
const int8_t *src = (const int8_t *)(data + offsetinBytes);
while(count--) { *dst++ = (intl6 t)(*src++"Ox80) << 8; } //笈制
written = sizeinBytes; //记汞写的數目

return written; II返回写的數目

由此可見, JNI 中利用本地的指針方式, 得到數据后拼湊成 Java 的啟据, 由此实現音


颜敷据流的佳諭。 晟然, Java: 扆亻考諭音頻敖据的效率是比絞低的。 因此, 呈然 Java 扆也有
敖据流的操作接口, 但是通常不在 Java 昆次迸行數据流操作。

-提示: AudioTrack 和 AudioRecord 奐只供特殊需要取得音颜敖据的亙用程序使用,


媒体播放和汞制等程序的音颜敖据都只从本地扆佳逯, 不令佳入 Java 屋。

2. Java 部分

android.media 中的几令奕提供了 Audio 部分的实瑰, 主要包括以下几令癸。

115�
•oo Android 板级支持与硬件相美子系统

• AudioFormat: Audio 格式的常量定又, 主要为通道 奕型。


• AudioSystem: Audio 系统奕, 全癸被 @hide 杯讠只, 不屑于 API 。
• AudioTrack: 用于放音的數据流操作。
• AudioRecord: 用于汞音的啟据流操作。
• AudioManager: Audio 的控制核心。
AudioManager 是 Java 扆音颜系统的控制核心, 使用 android.content 包中的 Context 奕

的 getSystemService() 方法, 以 AUDIO_SERVICE ("audio") 为參數调用可以荻得 介
AudioManager 的实例。
AudioManager 中的几令主要常量为音颜流 (Stream Type), 具有 STREAM_SYSTEM
(系统音颜流)、STREAM_RING (鈴声流)、STREAM_MUSIC (音示流)、STREAM_ALARM
(警扳流)、 STREAM_NOTIFICATION (通知)等几秤癸型。
AudioManager 的主要的几介方法如下所示:
public void adjustVolume(int direction, int flags) //调节音童
public vo這setMode(int mode) II没置音颜模式
public void setRingerMode(int ringerMode) II调节鈴声音纍
public vo這setStreamMute(int streamType, boolean state) II没置音颜流为靜音
public vo這setStreamSolo(int streamType, boolean state) II没置音颜流为单声道

AudioTrack 和 AudioRecord 分別用于放音和汞音的敖据流灶理,可以根据音颜流癸型、


釆祥率、通道等參數建立实例。 AudioTrack 的核心方法是 write(), AudioRecord 的核心方
法是 read()。 通迥送两令奕可以在 Java 扆対音颜系统迸行原始敖据流的渡/写操作。

7.3 音頻 BSP 的結枸

在 Android 系统中, Audio 杯准化部分是硬件抽象尼的接口,因此針対特定乎台, Audio


系统的移植包括 Audio 驱劫程序和 Audio 硬件抽象扆。
Audio 驱劫程序需要在 Linux 內核中实現, 呈然实現方式各昇, 然而在通常情況下,
Audio 的驱劫程序都需要提供用于音量控制等的控制奕接口, 用于 PCM 諭入、諭出的數据
奕接口。
Audio 硬件抽象扆 是 Audio 驱劫程序和 Audio 本地框架奕 AudioFlinger 的接口。 根据
Android 系统的接口定又, Audio 硬件抽象扆是 C++突的接口, 实現 Audio 硬件抽象扆需要
继承接口中定叉的三令奕, 分別用于惡体的控制 、 榆出和諭入。

�7.3.1 Audio驱劫程序
Audio 驱劫程序为 Linux 用户空阅提供 Audio 系统控制和數据的接口。
Linux 系统中, Audio 驱劫程序的框架有杯准的 OSS (Open Sound System, 玕放声音
系统) 和 ALSA (Advanced Linux Sound Architecture, 高级 Linux 声音体系) 框架。 但是在
具体实現的时候, 坯有各秤非柝准的方式。
从目前各今基于 Android 系统严品的情況米看, Audio 系统的实現方式也是各秤各祥

, 116
第7章 音頻系统 ooe

的, 并元统 一 的杯准。 但是各秤不同的 Audio 驱劫程序的功能大同小昇, 基本都需要包含


以下两令方面的功能。
圖控制接口: 音量控制、 靜音、 通道控制等功能。
.敷据接口: 需要支持 PCM CID粒中編碼调制)奕型的諭入和輸出。
在某些系统中, Audio 系统述是和硬件編解碼結合在一 起的, 因此可以直接迸行編碼
音颜的輸出和輸入。 例如, 直接榆出和榆入 MP3 、 AMR 格式的編碼音颜流。

1.oss 驱劫程序
oss 用于播放或呆制數字化的声音,其指杯主要有采祥速率(例如屯话为 8Kbps, DVD
为 96Kbps)、 channel 敖目(单声道、 立体声)、 釆祥分辨率 (8bit、 16bit)。
oss 驱劫是字符没各, 其主没各另为14, 次没各另由各介没各单袖定又。 oss 主要有
以下几秤没各文件。
e /dev/mixer: 次没各另为 o, 访祠声卡中內置的 mixer, 调整音量大小, 造掙音源。
e /dev/sndstat: 次没各另为 6, 潿试声卡, 涙迏令文件可以晁示声卡驱劫的信息。
e /dev/dsp C/dev/dspW、 /dev/audio): 次没各弓为3, 涙此没各迸行汞音, 写此没各迸
行放音。巨別在于釆祥的編碼不同, dsp 使用 8 位元符弓敖的紱性編碼, Audio 使用
µ律編碼(用于与 SunOS 的兼容), dspW 使用 16 位有符弓數的綫性編碼。
e /dev/sequencer: 次没各另为1, 访祠声卡內置的或者连接在 MIDI 端口的合成器。
e /dev/m.idiXX: 次没各另为 2 、 18、 34, MIDI 端口。
在用户空冏中, 最常用的是使用/dev/m.ixer 节燕迸行音量大小等控制, 使用 ioctl 接口,
/dev/dsp 用于音颜敷据操作, write 用于放音, read 用于汞音。
oss 音颜驱劫的架枸如囹 7-2 所示。
文件接口调 用(ioctVread/write)

用户空冏
/dev/mixer /dev/XXX


內核空冏

具体oss驱劫
-
(实現各介没各的struct file operations)
调用. i: 注冊
register_sound卫uxer
II

register_sound_dsp
register_sound_midi

(�::n�j�三:{;
调用T • 注冊

字符没各驱劫程序核心

硬件展
音颜没各

困7-2 oss 音颜驱劫的架杓

oss 驱劫程序的主要失文件如下所示。
117�
•oO Android 板级支持与硬件相美子系统

e include/linux/soundcard.h: OSS 驱劫的主要失文件。


e include/linux/sound.h: 定又 oss 驱劫的次没各另和注朋函數。
oss 驱幼程序为以下文件: sound/sound_core.c。
sound.h 用于 oss 各秤没各的注冊, 定又如下所示。
extern int register_sound_mixer(const struct file_opera豆ons *fops, int dev);
extern int register_sound_m這i(const struct file_opera户ons *fops, int dev);
extern int register_sound_dsp(const struct file_opera户ons *fops, int dev) ;
typedef int _bitwise snd_device_type_t;

基于 oss 驱劫程序架杓中,用户空阅主要使用/dev/mixer 迸行控制,使用/dev/dsp 迸行


敖据流的諭入和諭出。

2. ALSA驱劫程序
ALSA 是为音颜系统提供驱劫的 Linux 內核組件, 以替代原先的 oss。
ALSA 是 一
oss 那祥提供 一組內核驱劫
令完全玕放源代碼的音颜驱劫程序集, 除了像
程序模玦之外, ALSA 坯考fl为简化成用程序的編写提供相皮的函敖庠, 与 oss 提供的基
于 ioctl 等原始編程接口相比, ALSA 函敖庠使用起來要更加方便一 些。 利用垓函數庠, 玕
友人贝可以方便、快捷地升友出自己的皮用程序,细节則留給函數庠迸行內部外理。 ALSA
也提供了突似于 oss 的系统接口。 ALSA 的升岌者建汶座用程序玕岌者使用音颜函敖庠,
而不是直接调用驱劫程序。
ALSA 驱劫提供字符没各的接口, 其主没各另是 116, 次没各另由各令没各单狹定又,
主要的没各节熹如下所示。
a /dev/snd/control<N>: 主控制
a /dev/snd/pcm<N>c: PCM 捕荻 (capture) 通道
e /dev/snd/pcm<N>p: PCM 播放 (play) 通道
a /dev/snd/seq: 順序器
a /dev/snd/timer: 定时器
在用户空伺中, ALSA 驱劫通常配合 ALSA 庠米使用, ALSA 庠通迥 ioctl 等接口调用
ALSA 驱劫程序的没各节燕。 対于 ALSA 驱劫的调用, 通常调用用户空阅的 ALSA 庠的接
口, 而不是直接调用 ALSA 驱幼程序。

-提示: ALSA 驱劫程序可以支持模掠 oss 驱劫対用户空岡的接口, 打升两今內核造


璜之后, 可以同时提供 oss 的 dsp 和 m以er 改各节沌丶O
ALSA 音颜驱劫的架杓如囷 7-3 所示。
ALSA 驱劫程序的失文件如下。
a include/sound/asound.h: ALSA 驱劫的主要失文件。
e include/sound/core.h: ALSA 驱劫核心啟据結杓和具体驱劫的注冊函數。
a include/sound/pcm.h: 用于敖据通道 PCM 的染文件。
a include/sound/control.h: 用于控制的染文件。

, 118
第7章 音颜系统 ooe

ALSA用户空f司接口

文件接口调用(ioctl/read/write)

用户空阅

內核空f司
具体ALSA驱劫
(实瑰敖据没各和控制没各的結杓)

硬件


操作
调用

字符没各驱劫程序

硬件扆
音颜没各

胆7-3 ALSA音颜驱劫的架杓

• include/sound/soc.h: 芯片扆的失文件。
ALSA驱幼程序的核心实瑰包括在sound/core/目泵中的sound.c、 pcm.c和control.c等
几令文件中。
対ALSA在用户空岡的没各文件的操作主要使用涙、 写和ioctl. asound.h中相美的內
容如下所示:
韭define SNDRV_DEV_TOPLEVEL ((_force snd_device_type_t) 0)
#define SNDRV DEV CONTROL (( force snd device type t) 1)
#define SNDRV_DEV_LOWLEVEL_PRE ((_force snd_device_type_t) 2)
#define SNDRV_DEV_LOWLEVEL_NORMAL ((_force snd_device_type_t) OxlOOO)
#define SNDRV DEV PCM (( force snd device type t) OxlOOl)
謩define SNDRV_DEV_RAWMIDI ((_force snd_device_type_t) Oxl002)
#define SNDRV DEV TI琿R (( force snd device type t) Ox1003)
#define SNDRV_DEV_SEQUENCER ((_force snd_device_type_t) Oxl004)
#define SNDRV DEV HWDEP ((_force snd_device_type_t) OxlOOS)
#define SNDRV DEV INFO (( force snd device type t) Ox1006)
#define SNDRV DEV BUS ((_force snd_device_type_t) Oxl007)
#define SNDRV DEV CODEC (( force snd device_type_t) Oxl008)
#define SNDRV DEV JACK ((_force snd_device_type_t) Oxl009)
#define SNDRV_DEV_LOWLEVEL ((_force snd_device_type_t) Ox2000)
int snd_register_device_for_dev(int type, struct snd_card *card,
int dev, const struct file_operations *f_ops,
void *private_data, const char *name, struct device *device);
static_inline int snd_;:eqisteL_device(int type, s主r.1:.1ct s9止ca旦*cc¼_這,_if!t dev .,1,

119�
• O o Android 板级支持与硬件相美子系统

canst struct file_operations *f_ops, vo這*private_data,


canst char *name) {}
int snd_unr_egister_device (int type, struct 旦nd_card *card, int_dev);

ALSA 没各实現后, 使用 snd_register_device_ for_dev()迸行注朋, 用户空冏的没各节熹


則根据实現的情況严生。
soc.h 中定又芯片级別的实瑰, 几令核心結枸如下所示。
• snd_soc_pcm_stream 結杓: 表示声卡的敖据流。
• snd_soc_ops 結枸: 表示声卡的操作, 包括玕、 美、 參數没置等。
• snd_soc_codec 結杓: 表示声卡的編解碼, 包括了主要的敖据結杓。
• snd_soc_dai_link 結杓: 表示声卡中 DA 的接口, 其中包含了 snd_soc_ops。
• snd_soc_card 結杓: 表示一 今声卡, 其中包含了 snd_soc_dai_link 的數組。

• snd_soc_device 結杓: 表示 介声卡的没各接口。
一令具体平台的声音驱劫尼的实瑰, 要根据 soc.h 中定乂的內容完成。 例如, soc 的
通用函數 snd_soc_new_pcms() 调用了 snd_pcm_new(), 迸而调用 ALSA 核心建立 PCM 没各,
snd_soc_cnew() 則调用 snd_ctl_new l ()函數建立控制没各。

�7.3.2 硬件抽象扆的內容

1. Audio 硬件抽象展的主要接口

Audio 的硬件抽象尼是 AudioFlinger 和 Audio 硬件之阅的尼次, 在各介系统的移植迥


程中可以有不同的实現方式。 Audio 硬件抽象扆和 AudioFlinger 中使用的枚举哭型在 Audio
框架炎的染文件 AudioSystem.h 中定叉。
AudioHardwarelnterface.h 中定又了三介炎: 用于敖据榆出的 AudioStreamOut 、 用于敷
据輸入的 AudioStreamln 和用于核心管理的 AudioHardwarelnterface。
AudioStreamOut 奐用于描述音颜諭出的没各, 迏令接口的主要定又如下所示:
class AudioStreamOut (
public:
virtual -AudioStreamOut() = 0;
// . . . .. . 省略荻取函數: sampleRate() bufferSize() channels() format() frameSize()
virtual status_t setVolume (float left, float right) = 0;
virtual ssize_t write(const void* buffer, size_t bytes) = 0;
virtual status_t standby() = 0;
virtual status_t dump(int fd, const Vector<Stringl 6>& args) = 0;
virtual status_t setParameters(const StringB& keyValuePairs) = 0;
virtual String8 getParameters(const String8& keys) = 0;
virtual status t getRenderPosition(uint32_t *dspFrames) = 0;
』· ·

. '


AudioStreamOut 主要的函數是 writeO, 其參數就是 令內存的指針和低度, 表示用于
輸出的音颜啟据。 由实珗者通迥实际的音颜硬件没各, 将迏抉內存諭出, 也就实現了音頻
的播放。 迏玦內存的內容是不可被实瑰者更改的(因此为 const)。
AudioStreamln 奕用于描述音颜的榆入没各, 迏今接口的主要定又如下所示:
class AudioStreamin {

�120
第7章 音頻系统 oo•

public:
virtual -AudioStreamin() = 0;
II省略荻取函蚊 sampleRate() bufferSize() channels() format() frameSize()
virtual status_t setGain(float gain) = 0;
virtual ssize_t read(void* buffer, ssize_t bytes) = O;
virtual status_t dump(int fd, const Vector<Stringl6>& args) = O;
virtual status_t standby() = 0;
virtual status_t setParameters(const String8& keyValuePairs) = 0;
virtual Strings getParameters(const StringB& keys) = 0;
virtual unsigned int getinputFramesLost() const = O; II荻得丟失的帔致目
};

A呻oStreamOut主要的函敖是readO, 其參敖就是 介內存的指針和板度, 表示用于
榆入的音颜敷据。 由实現者从实际的音颜 硬件没各中荻取音颜數据, 填充迏坎內存。
AudioStreamOut 和 AudioStreamln迏两介癸都需要通迥Audio 的硬件抽象扆核心
A呻oHardwarelnterface 接口炎得到。 AudioHardwarelnterface突的定又如下所示:
class AudioHardwareinterface {
public:
virtual -AudioHardwareinterface() {}
virtual status_t initCheck() = 0;
virtual status_t setVoiceVolume(float volume) = 0; //没咒音鼠
virtual status t setMasterVolume(float volume) = O;
virtual status_t setMode(int mode) = 0; //包括正常、 鈴声和屯话模式
virtual status t setMicMute(bool state) = 0; //没置変克风的靜音
virtual status t getMicMute(bool* state) = 0;
//没置各朴參敷
virtual status t setParameters(const String8& keyValuePairs) = O;
virtual String8 getParameters(const String8& keys) = 0;
virtual size t getinputBufferSize(uint32_t sampleRate,
int format, int channelCount) = 0;
virtual AudioStreamOut* open0utputStream(uint32 t devices, II打升榆出流
int *format =O, uint32_t *channels= O,
uint32_t *sampleRate=O, status_t *status=O) = O;
virtual void closeOutputStream(AudioStreamOut* out) = O;
virtual AudioStreamin* openinputStream( uint32 t devices, II打升榆入流
int *format, uint32_t *channels,
uint32_t *sampleRate, status_t *status,
AudioSystem::au中o_in_acoustics acoustics) = 0;
virtual void closeinputStream(AudioStreamin* in) = 0;
virtual status_t dumpState(int fd, const Vector<Stringl6>& args) = 0;
static AudioHardwareinterface* create();
};

AudioHardwarelnterface的openOutputStreamO和openlnputStream()两介函數分別用于荻
取AudioStreamOut和AudioStreamln的实例, 它們作为音颜諭出和輸入 没各米使用, 共同

的參敖包括音颜的格式、 通道、 釆祥率等几介方面。 setMode()是 令重要的没置參數,
NORMAL用于正常的音颜播放,RINGTONE用于鈴声播放,IN_CA LL用于屯话呼叫這程。

AudioHardwarelnterface.h定又了C语言的接口米荻取 介AudioHardwarelnterface 癸
型的指針, 此函敷如下所示:
extern "C" AudioHardwareinterface* createAudioHardware(void);

如果实現 今Android 的硬件抽象扆, 則需要 实現以上的迏三介癸, 将代碼編诵生成

121�
•oo Android板级支持与硬件相美子系统

幼态庠 libauido.so。 在正常 情況下,Au山oFlinger佘镱接迏介幼态庠,并调用 其 中 的


create
AudioHardwareO函致米 荻取所实現的AudioHardwarelnterface接口 。
Audio的硬件抽象尼就是要继承实瑰接口中的AudioHardwareinterface、AudioStreamln
和AudioStreamOut迏三介突。 AudioHardwarelnterface負靑惡控,AudioStreamln奐靑敷据
流的榆入 ,AudioStreamOut奐靑敖据流的諭出。
Audio硬件抽象居的实現通常需要生成劫态庠libaudio.so。 根据Au由o系统的特燕, 硬
件抽象扆也需要考忠敖据流和控制流两令部分 。相対亻考感器、 GPS 等系统,Audio 系统的
啟据流是比絞大的 PC M敷据 A
。 udio系统的控制接口最主要的部分是音量控制,根据Audio
系统的不同,坯包含了各神不同參數的没置 。
Audio硬件抽象扆的实瑰有以下几令內容 。
e Audio參敖的祠題
AudioHardwarelnterface、AudioStreamln 和 AudioStreamOut 迏三介癸都 包 含 了

setParameters和 getParameters接口没置和荻取系统的參敖, 些杯准參數由AudioSystem.h
中的AudioParameter癸米表示:主要包含了Audio路往没各、 采祥率、 格式、 通道 、 幀敷
目等 。 在 AudioStreamln和 AudioStreamOut 中,如果不支持某秤哭型的參數,需要返回
INVALID_OPERATION。 涉及參數能否更改、 能否立刻生效等祠題,都需要根据硬件的具
体情況來灶理 。
e AudioHardwarelnterface::setModeO实現的阅題
setModeO用于没置系统的模式, 由AudioSystem.h中的AudioSystem: :audio_ mode米表
示, 包含了 MODE_NORM
AL(通 常表示音示播放)、 MODE_RINGTONE(伶声)、
MODE_IN_CALL C呼入屯话 )等敖值。屯话和鈴声的部分实际上已统涉及Audio硬件之外
的硬件 。 没置的效果由具体乎台的硬件控制情況決定 。
e AudioBufferSize的同題
在实珗Audio系统时, 根据音颜格式、采祥率、通道數目, 可以得到每 一 令幀( frame)
的大小和硝率。 BufferSize的大小決定可以緩沖多板时伺。 如果BufferSize迥小, 有延迟的
时候将佘严生声音阅新的阅題;如果BufferSize迥大,将佘严生控制不炅敏的祠題 。因此,
需要根据系统的实际情況,确定BufferSize的大小 。
圖与藍牙的美系
从Android 的 AudioFlinger实現情況來看, 盔牙部分涉及音颜的功能可以由 一 介名稔
为A2dpAudiointerface的炎调用Bluez接口來灶理,它本身也 是 一 令AudioHardwarelnterface
的 继承实現者。在以前的Android版本中,藍牙的A2dpAudiointerface与主Audio硬件抽象
扆并列。 AudioFlinger也分別灶理了主Audio硬件抽象扆和A2dpAudiolnterface的清況 。在
絞新的Android版本中, 主Audio 硬件抽象尼可以有造掙地利用迏令文件实現盔牙方面的
功能 。A2dpAudiolnterface的功能不是单袖存在的,厲于附加的功能, 它将封裝调用主Audio
``
硬件抽象扆,并 截流"一些功能使用自己的实現 。

2. Audio硬件抽象扆的策略接口
Aduio的策略是 一 介晡助Audio系统的功能模坎,其內容在AudioPolicyinterface.h文件

, 122
第7章 音頻系统 ooe

中定叉。 Audio策略接口由奕似硬件抽象扆的模玦实現, 被AudioFlinger调用。


AudioPolicylnterface.h文件中定又了AudioPolicylnterface和AudioPolicylnterfaceClient
迏两令接口 癸, 两令対外的接口如下所示:
extern "C" AudioPolicyinterface* createAudioPol-icyManager(
AudioPolicyClientinterface *clientinterface);
extern "C" void destroyAudioPolicyManager(AudioPolicy一Inte�旦:ace *丑辻er臣芪e);

接口的调用遷緝是使用AudioPolicylnterfaceClient作为參數來創建AudioPolicylnterface
災AudioPolicylnterface是音颜策略操作的主要接口。
AudioPolicylnterface接口突的定又如下所示:
class AudioPolicyinterface{
public:
virtual -AudioPolicyinterface () {)
virtual status_t setDeviceConnectionState(AudioSystem:: audio_devi"ces device,
AudioSystem::device_connection_state state,
const char *device_address) = O;
virtual AudioSystem::device_connection_state
getDeviceConnectionState(AudioSystem::audio_devices device,
const char *device_address) = O;
virtual void setPhoneState(int state) = 0; II没置屯话的狀态
virtual void setRingerMode(uint32_t mode, uint32_t mask) = O; II没置伶声狀态
virtual void setForceUse(AudioSystem::force_use usage, //没置強行使用的通道
AudioSystem::forced_config config) = 0;
virtual AudioSystem::forced_config getForceUse(
AudioSystem::force_use usage) = O;
virtual·void setSystemProperty(canst char* property, canst char* value) = O;
// Audio榆入和榆出路往相哭功能
virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
uint32_t samplingRate = 0,
uint32_t format = AudioSystem::FORMAT_DEFAULT,
uint32_t channels = 0,
AudioSystem::output_flags flags
= AudioSystem::OUTPUT_FLAG_INDIRECT) = 0;
virtual status_t startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream) = 0;
virtual status_t stopOutput(audio_io_handle_t output,
AudioSystem::strearn_type stream) = O;
virtual void releaseOutput(audio_io_handle_t output) = O;
virtual audio_io_handle_t getinput(int inputSource,
uint32_t samplingRate = 0,
uint32_t Format = AudioSystem::FORMAT_DEFAULT,
uint32_t channels = 0,
AudioSystem::audio_in_acoustics acoustics =
(AudioSystem::audio_in_acous已cs)O) = 0;
V 訌 rtual status_t startinput(audio_io_handle_t input) = O;
virtual status_t stoplnput(audio_io_handle卫input) = 0;
virtual void releaseinput(audio_io_handle_t input) = O;
II省略音童控制和其他操作函敷
};

AudioPolicy Interface炎由一 些抽象接口來实現, 送令癸主要包含了基本配置、 路往没


置、 音量没置几介方面的功能。AudioPolicylnterface的大部分接口 是按照没置一荻取(set
和 get)成対的失系。 其中实現的 一 令核心函敖为setForceUse(), 其中的第一介表示指定所

123�
• O O Android 板级支持与硬件相美子系统

使用的奕型 (force_use), 第二令參敖为被強行使用的配置 (forced_config), 基本就是音颜


敖据流的通道。
几介和輸入/諭出相芙的函數使用 audio_io_handle_t 奕型表示一介音颜榆入没各或者榆
出没各的句柄。 諭入/榆出坏节的 start 和 stop 函敖将在 Audio 的上扆核心坏节中被调用。
AudioPolicylnterface 隔禽 Audio 系统的核心部分和鋪助性的功能部分。例如,由于 Audio
系统和屯话系统有美系, 因此 setPhoneState()接口是相美屯话的没置部分; Audio 系统可以
有多介榆出和榆入, setForceU 溟)接口的功能強行没置榆出和諭入的通道。

O 提示: Android 系统的框架屋令根据汤景调用 Audio 策略的没置接口,該設置能否生


效則由硬件抽象屋的实現決定。

实現 AudioPolicyInterface 需要結合自身系统硬件的特,亞米实珗, 不仅涉及 Audio 系统


本身, 坯涉及藍牙系统(有美 A2DP) 、 屯话部分, 以及系统特,亞的外围甩路等內容。
AudioPolicylnterface 的各令接口在上尼被调用, 相昚于対硬件控制的钓子, 在具体实瑰的
迥程中需要根据自身系统完成。

7.4 音頻BSP的实現

�7.4.1 通用的Audio系统实瑰
Android 的 AudioFlinger 工程 中 的 Android.ml< 可 以生成 libaudiointerface.a 和

libaudiopolicybase.a 靜态庠,分別是音颜和音颜策略的硬件抽象扆的 神通用实現。在配置
宏 BOARD_USES_GENERIC_ AUDIO 为 true 的时候, 迏令通用的音颜硬件抽象尼被启用,
作为基本的实現方式。
其中的几今文件各自表示一令硬件抽象尼, 如下所示。

e AudioHardwareStub: 实現 Audio 硬件抽象扆的 令粧。
e AudioDumplnterface: 实現以文件为諭入/諭出的 Audio 硬件抽象扆。
e AudioHardwareGeneric: 实現基于特定驱劫的通用 Audio 硬件抽象扆。
在 AudioHardwarelnterface.cpp 中, 实 現了 Audio 硬 件 抽 象 扆 的刨建函 敖
AudioHardwarelnterface::create(), 內容如下所示:
AudioHardwarelnterface* AudioHardwarelnterface: : create() {
AudioHardwareinterface* hw = O;
char value[PROPERTY VALUE MAX];
庄 fdef GENERIC AUDIO
hw = new AudioHardwareGeneric();
itelse
if (property_get("ro.kernel.qemu", value, 0)) {
hw = new AudioHardwareGeneric(); II使用通用的Audio硬件抽象展
) else { hw = createAudioHardware();) II正常使用硬件抽象展
itendif
if (hw->initCheck() != NO一ERROR) {
delete hw;
hw =_new AudioHardwareStub(); I_/_建立Stub硬件抽象展

�124
第7章音頻系统 ooe

hfdef WITH A2DP


hw = new A2dpAudiointerface(hw); II建立盔牙A2DP的Audio硬件抽象昆
#endif
#ifdef ENABLE AUDIO DUMP
hw = new AudioDumpinterface(hw); II使用实际的Audio的Dump接口实現, 替換hw
#endif
return hw ;

最后返回的接口均为 AudioHardwarelnterface 奕型的指針,迏是由几介宏米控制的。使


用 GENERIC_AUDIO 、ENABLE_AUDIO_DUMP 等宏,可以造拌不 同的 Audio 硬件抽象扆。
如果 GENERIC_AUDIO 为 true, 建立 Generic 的 Audio 硬件抽象尼,否則将建立粧实現的
Audio 硬件抽象屄。如果 ENABLE_AUDIO_DUMP 为 true, 将使用 Dump 功能的 Audio 硬
件抽象扆。同时,如果 WITH_A2DP 为 true, 将在原有的基絀上,附加 A2dpAudiolnterface
的 Audio 硬件抽象扆。

1. 用粧实瑰的 Audio 硬件抽象居

AudioHardwareStub.h 和 AudioHardwareStub.cpp 是 一介 Android 硬件抽象扆的粧实珗方


式。迏令实瑰不操作实际的硬件和文件,所迸行的是空操作,在系统没有实际的 Audio 没
各时 使用迏令实現米保证系统的正常工作。如果使用迏今硬件抽象屄,实际上 Audio 系统
的榆入和諭出都将为空。
Auido 硬件抽象屄的粧实瑰使用了最简单模式,只是用固定的參敖(緩沖巨大小、釆
祥率、通道數),并将一 些函數直接元錯课返回。
AudioHardwareStub 是 一介实珗的炎它继承了 AudioHardwareBase, 实际上也就是继
承 AudioHardwarelnterface。迏介炎的內容如下所示:
class AudioHardwareStub : public AudioHardwareBase {
public:
AudioHardwareStub();
virtual -AudioHardwareStub();
virtual status_t initCheck();
virtual status_t setVoiceVolume(float volume);
virtual status_t setMasterVolume(float volume);
virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; }
virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO ERROR; }
virtual AudioStreamOut* openOutputStream( //打玕榆出流
uint32_t devices, int *format=O, uint32_t *channels=O,
uint32_t *sampleRate=O, status_t *status=O);
virtual void closeOutputStream(AudioStreamOut* out);
virtual AudioStreamin* openinputStream( //打升榆入流
uint32_t devices, int *format, uint32_t *channels,
uint32_t *sampleRate, status_t *status,
AudioSystem::audio_in_acoustics acoustics);
virtual void closeinputStream(AudioStreamin* in);
II省略部分內容
};

在 AudioHardwareStub 癸中,打玕諭出流的函敖为 openOutputStreamO, 实現如下所示:


AudioStreamOut* AudioHardwareStub::open0utputStream(uint32_t devices, int *format,

125�
•oO Android 板级支持与硬件相美子系统

uint32一_t *channels, uint32_t *sampleRate, status_t *status) {


AudioStreamOutStub* out = new AudioStreamOutStub(); //建立突
status_t lStatus = out->set(format, channels, sampleRate); //没賀格式通道采祥率
江(status) { *status = lStatus; } //返回狀态值
if (lStatus == NO_ERROR) return out;
delete out;
return 0;

openOutputStream()实际上就是新建了 一介 AudioStreamOutStub 奕并没置參數,然后将


其指針返回。 用于打玕榆入流的 openlnputStream() 的实瑰与之癸似。
在 实 瑰迥程中 , 为了保证声 音可以輸入和輸出, 迏 介粧 实現 的主要 內 容是实現
AudioStreamOutStub 炎的写函數和 AudioStreamlnStub 炎的漾函數, 如下所示:
ssize_t AudioStrearnOutStub::write(const void* buffer, size_t bytes) (
usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sarnpleRate());
return bytes;

ssize_t AudioStreaminStub::read(void* buffer, ssize_t bytes) {


usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate());
memset(buffer, 0, bytes);
return bytes;

由此可見, 使用迏介接口迸行音颜的輸入和榆出时, 和真实的没各没有美系。 対于輸


出的情況, 不合有声音播出, 但是返回值表示全部內容已經榆出完成; 対于諭入的情況,
将返回全部为O的敖据。 諭出和輸入的延时和數据大小有美。

2. 提供 Dump 功能的 Audio 硬件抽象扆

AudioDumplnterface.b 和 AudioDumplnterface.cpp 是一 令提供了 Dump 功能的 Audio 硬


件抽象居。 Dump 的含乂为傾倒, AudioDumplnterface 的功能是以文件模姒 Audio 硬件流的
榆出和榆入坏节。
AudioStreamOutDump 的主要內容也是奐賣建立諭入流和輸出流。迏两介流最終都是通
迥文件系统中的文件米实現的。与粧实現不同, Audio Dump 硬件抽象扆的实現可以姓理不
同參數(格式、 通道、 采祥率等)。
在实現文件 AudioDumplnterface.cpp 中, AudioStreamOut 哭所实現的写函數中的內容
如下所示:
ssize t AudioStreamOutDump::write{const vo這* buffer, size t bytes) {
ssize t ret;
//省略部分內容
if(!mOutFile) {
if (minterface->fileName() ! = "") { II比絞文件的名稔
char name[255];
sprintf(name, "%s_%d_%d.pcm", //即得榆出的文件名
minterface->fileName().string(), rnid, ++rnFileCount);
mOutF訌e = fopen(name, "wb"); //打升文件(实际是新建可滾写文件)

if (mOutFile} (fwrite(buffer, bytes, 1, mOutFile}; } // 写入文件


return ret;

�126
第7章 音頻系统 ooe

迸行Audio諭出操作的时候,将生成名为 mFileName_{ mld}_{ mFileCount}.pcm的榆


出文件, 迏介文件本身就是 一 介仅包含PCM流的數据文件。如果文件没有被打升, 則首先
打升文件; 如果文件已經打玕, 則直接写迏秤文件。
AudioDurnplnterface.cpp的 AudioStreamOut所实瑰的埃函數的內容如下所示:
ssize t AudioStreaminDump::read(void* buffer, ssize_t bytes) (
II省略部分內容
usleep((bytes * 1000000) I frameSize() I sampleRate());
if(!rninFile) {
char name[255J;
strcpy(name, "lsdcardlmusiclsine440"); II諭入文件的路往和基本名杯
江(channels() == AudioSystem: :CHANNEL_IN_MONO) ( II单声道和立体声
strcat(name, "_mo");
) else {
strcat(name, "_st");

if (format() == AudioSystem::PCM_16_BIT) { II不同格式的姓理


strcat(name, "_16b");
I else {
strcat(name, "_Sb");

if (sampleRate {) < 16000) ( II不同釆祥率的灶理


strcat(name, "_8k");
} else if (sampleRate() < 32000) {
strcat(name, "_22k");
} else if (sampleRate {) < 48000) (
strcat(name, "_44k");
} else {
strcat(name, "_48k");

strcat(name, ".wav"); II文件后綴名为.wav


minFile = fopen(name, "rb"); II打升榆入文件
if (minFile) {
fseek(minFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); II移幼出wav文件失

if (minFile) {
ssize t bytesRead = fread(buffer, bytes, 1, minFile); II从文件中渙敷据
if (bytesRead != bytes) {
fseek(minFile, AUDIO DUMP WAVE HDR SIZE, SEEK SET); II移劫文件位置
fread((uintB_t *)buffer+bytesRead, bytes-bytesRead, 1, minFile);

return bytes;

淙函致是将文件系统中的 一 介文件作为Audio的諭入流, 迏里使用的是WAY文件。


由于WAY 文件本身的主題就是PCM 流, 仅仅是包含了 一 介很小的文件�. 因此在打升
WAY文件的时候, 首先需要调用fseek()略迥迏介文件失。实际上, 不略迥文件失也是可以
的 , 只是在升 失多 一 些噪音。 迭掙文 件的路往为/sdcard/music/, 文 件 名林的格式为

sine440_{通道}-{格式}_{采祥率}.wav。 例如, 介单声道、PCM16格式、 44k釆祥率的文
件名为 sine440 _mo_I 6b_44k. wav。
Audio Dump硬件抽象尼很适合用于调试。 例如, 卉迸行音颜播放器调试时, 有时元

127�
•oo Android 板级支持与硬件相美子系统

法确从是解碼器的祠題坯是 Audio 榆出单元的祠題, 送时就可以用迏令癸米替換实际的


Audio 硬件抽象扆,将解碼器榆出的 Audio 的 PCM 敷据写入文件中, 由此可以判斷解碼器
的榆出是否正确。 在验证諭入的时候,迥程奕似,需要准各相皮的WAY文件,并改成相皮
的名字, 夏制到目杯文件系统的/sdcard/music/ 目呆中。

3. 通用的 Audio 硬件抽象展

AudioHardwareGeneric.h 和 AudioHardwareGeneric.cpp 是 Android 通用的一介 Audio 硬


件抽象扆。 与前面的粧实現不同, 迏是一 介真正能夥使用的 Audio 硬件抽象扆, 但是它需

要 Android 的 秤特殊的声音驱劫程序的支持。
与前面癸似,AudioStreamOutGeneric 、 AudioStreamlnGeneric 和 AudioHardwareGeneric
迏三介奕分別继承 Audio 硬件抽象扆的三令接口。通用 Audio 諭出癸的接口使用固定參數:
44k 采祥率、 立体声、 PCM16 格式;通用 Audio 榆入癸的接口使用固定參數 8k 采祥率,单
声道, PCM16 格式。
在 AudioHardwareGeneric.cpp 中, 定又没各的路往如下所示:
static char const * const kAudioDeviceName = "/dev/eac";

EAC 是 Android 定叉的简易 Audio 驱劫程序。 在用户空阅, eac 没各在文件系统的节


燕为 /dev/eac。 作为 Android 的通用音頻驱劫的实現, 写 eac 没各可以放音, 涙 eac 没各可
以汞音。 放音和汞音的數据是通這模姒器从主机得到的。
在 AudioHardwareGeneric 的杓造函數中, 打升迏介驱劫程序的没各节熹, 如下所示:
AudioHardwareGeneric::AudioHardwareGeneric()
: mOutput(O), mlnput(O), mFd(-1), rnMicMute(false) {
mFd = ::open(kAudioDeviceName, O_RDWR); //打玕通用音颜没各的节 ,'i.i.: /dev/eac

送介音颜没各是一令比絞简单的驱劫程序,没有很多接口 (ioctl 命令),只是用写没各


迸行汞音, 淩没各迸行放音。 放音和汞音支持的都是 16 位的 PCM。
ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) (
Mutex::Autolock _l(mLock);
return ssize t(::write(mFd, buffer, bytes)); II写入硬件没各

ssize_t AudioStreaminGeneric::read{void* buffer, ssize_t bytes) {


AutoMutex lock{mLock);
if (mFd < 0) { return NO_INIT;)
return : : read(mFd, buffer, bytes); // 1*取硬件没各

通用 Audio 硬件抽象扆是 一 介可以真正工作的 Audio 硬件抽象扆, 但是迏秤实現方式


非常简单, 不支持各神没置, 參數也只能使用默从的。
如果想在真正的硬件系统中使用迏秤方式, 只需要能以涙、 写表示汞音和放音的没各
节熹 /dev/eac, 并且要支持硬件抽象扆中固定的參敖, 內核空岡中只需要有能杓建迏介节煮
的相皮驱劫。

4. Audio 的策略实瑰

AudioPolicyManagerBase.cpp 是音颜策略硬件抽象扆的实現。 AudioPolicyManagerBase

, 128
第7章 音頻系统 ooe

哭是 AudioPolicylnterface 的继承者。

setForceUseQ是其中实現的 介核心函數, 如下所示:
void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage,
AudioSystem::forced_config config) {
bool forceVolumeReeval = false;
switch(usage) {
case AudioSystem::FOR_COMMUNICATION: //通信汤呆: 対话筒、 盔牙语音等有效
if {config != AudioSystem::FORCE_SPEAKER &&
config != AudioSystem::FORCE_BT_SCO &&
config != AudioSystem::FORCE_NONE) {
return;

mForceUse[usage] = config;
break;
case AudioSystem::FOR_MEDIA: //媒体汤呆: 対耳机、 盔牙ATOP、 有鈛附件等有效
江(config != AudioSystem::FORCE_HEADPHONES &&
config != AudioSystem::FORCE_BT_A2DP &&
config != AudioSystem::FORCE_WIRED_ACCESSORY &&
config != AudioSystem::FORCE_NONE) {
return;

mForceUse[usage) = config;
break;
//省略其他汤燝

//省略A2DP的部分內容
uint32_t newDevice = getNewDevice(mHardwareOutput, false);
updateDeviceForStrategy();
setOutputDevice(mHardwareOutput, newDevice); //没置榆出没各
if (forceVolumeReeval) { applyStreamVolumes(mHardwareOutput, newDevice); }
audio io handle t active Input = getActiveinput();
if (activeinput != 0) { //判斷激活的榆入没各
AudioinputDescriptor *inputDesc = minputs.valueFor(activeinput);
newDevice = getDeviceForinputSource(inputDesc->minputSource);
if (newDevice != inputDesc->mDevice) {
inputDesc->mDevice = newDevice; //没置目杯的榆入没各
AudioParameter param = AudioParameter();
param.addint(StringB{AudioParameter::keyRouting), (int)newDevice);
mpClientinterface->setParameters(activeinput, param.toString());

mForceUse 是 一 介癸型为 AudioSystem:: forced_config 的致組, 配置保存在各介士j)景配


置的參數, 没置到其中的內容就是強制使用的没各。 例如, 在媒体汤景汤景中, 可以没置
榆出为耳机或者盔牙的 A2DP 通道。 策略实現者則根据參敖, 対榆入/諭出迸行没置。
失文件 AudioPolicyManagerBase.h 中定叉的 rou皿g_strategy 奕型表示的是音颜通道的
连接策略, 在 getOutputO和 getlnput() 函敖中, 将根据策略得到相成的没各。

�7.4.2 基于 oss 的实瑰方式


oss 驱幼程序的实現方式, 重燕在于硬件抽象扆和驱劫程序的交互。敖据流的渡/写操
作通迥対/dev/dsp 没各的懷/写米完成;巨別在于 oss 支持了更多的 ioctl 迸行没置,坯涉及

129�
•oo Android板级支持与硬件相美子系统

通迥/dev/mixer没各迸行控制, 并支持更多不同的參數。
基于 oss 的硬件抽象昆結枸如囹7-4所示。
set/get open/close write read

OSS Audio HAL

ioctl ioctl

I tdev/mixer 丨
困7-4 基于 oss 的硬件抽象展

�7.4.3 基于ALSA的实瑰方式
対于ALSA驱劫程序, 实現方式 一 般不是直接调用驱劫程序的没各节魚, 而是先实現
用户空岡的alsa-lib, 然后Audio硬件抽象尼通迥调用alsa-lib米实瑰。
基于ALSA的硬件抽象展結杓如囹7-5所示。

ALSA Audio HAL

snd_pcm_readi
snd_pcm_XXX

asoundlib .h

alsa-lib

ioctl ioctl ioctl

/dev/snd/coalrolC<N> /elev廩ld/pcm<N>p 心這在m<N>c

囹7-5 基于ALSA的硬件抽象扆結杓

在Android代碼中, 已經包含了Alsa Audio部分的实現, 主要包含ALSA庠、 ALSA


工具和ALSA硬件抽象扆3介部分。 迏些內容已經在公共的代碼合庠中, 但是没有包含在
统 一的 repo中, 需要单秈clone。

, 130
第7章 音頻系统 ooe

荻取 ALSA 庠內容如下所示:
,git clone git://andr_oid .git . kernel. org/platform/exter_nal/alsa-J,ib._git

荻取 ALSA 工具內容如下所示:
git clone git://android .git .kernel.org/platform/external/alsa-utils.gi七

Android 中 alsa-lib 和 alsa-utils 的內容与玕源的 ALSA 工程奕似, 系统的最終实現只需


要 alsa-lib 。 alsa-utils 基于 alsa-lib 的工具可以用于调试, 主要生成 alsa_aplay、 alsa amixer
和 alsa_ctl 迏三今可执行程序, 基本使用方法如下所示:
$ alsa_aplay [OPTION] .. . [FILE] . . .
$ alsa_amixer <options> [command]
$ alsa_ctl <options> cg阿nand

它亻i'J 的功能是:利用 alsa_aplay 迸行播放 PCM (使用文件作为榆入, 可指定參敖), 利


用 alsa_a皿xer 可以劫态没置音量和荻取信息等內容,利用 alsa_ctl 可没置全局配置等內容。
例如, 可以按照如下方式使用:
1f ./alsa_aplay -t wav test.wav 1f測试播放test.wav : 文件
1f ./alsa_amixer controls 1f列出者前alsa-lib中接口

荻取基于 ALSA 的硬件抽象屄的方法如下所示:


gi! clone git://android .git.kernel .org/platform/hardware/alsa_sound.git

荻取的內容为 alsa_sound 目汞, 其中主要包含了以下文件, 內容如下所示。


e AudioHardwareALSA.h: ALSA 硬件抽象扆的失文件。
e AudioHardwareALSA.cpp: ALSA 硬件抽象尼的惡控实瑰。
e AudioStreamOutALSA.cpp: ALSA 硬件抽象扆的輸出坏节。
e AudioStreamlnALSA.cpp: ALSA 硬件抽象屄的諭入坏节。
e ALSAStreamOps.cpp: 鋪助文件, ALSA 的流操作。
e ALSAControl.cpp: 輔助文件, ALSA 的控制。
e ALSAMixer.cpp: 晡助文件, ALSA 的混音器。
e AudioPolicyManagerALSA.h: Au山o 策略管理染文件。
e AudioPolicyManagerALSA.cpp: Audio 策略管理的实現。
alsa_sound 工程实現了 一 介基于 alsa-lib 的、 功能完各的硬件抽象尼, 在絞高版本的实
現中, 坯有插件支持以适皮不同的系统。
alsa_sound 中主要失文件的实現 AudioHardwareALSA.b 中包含如下內容:
和 incl.µ壺<alsa/asoundlib.h>

asoundlib.h 就是 ALSA 庠的染文件, 在 ALSA 的硬件抽象扆中, 不対 ALSA 系统没各


节燕迸行直接的操作, 而是操作 ALSA 庠。
在 ALSAMixer.cpp 中的 ALSAMixer: :setMasterVolume() 函數用于主音量的控制, 被硬
件抽象屋的 setMasterVolume()接口调用, 內容如下所示:
status t ALSAMixer::�etMasterVolume(float v:olume) I

131�
•oo Android 板级支持与硬件相美子系统

mixer_info_t *info = rnixerMasterProp[SND_PCM_STREAM_PLAYBACKJ .minfo;


if (!info I I ! info->elem) return INVALID_OPERATION;
long minVol = info->rnin;
long rnaxVol = info->max;
long vol = minVol + volume * (rnaxVol - minVol);
if (vol > rnaxVol) vol = rnaxVol;
if (vol < rninVol) vol = rninVol;
info->volurne = vol;
snd mixer selem set playback volume all (info->elern, vol); //调用alsa-訌b
return NO ERROR;

其中调用的 snd_mixer_selem_set_playback_volume_all() 函數, 即使用 alsa-lib 的接口米


完成控制突的操作。
AudioStreamOutALSA.cpp 中的 AudioStreamOutALSA::write()用于实現写操作,其主題
內容如下所示:
ssize t AudioStreamOutALSA::write(const void *buffer, size t bytes) {
AutoMutex lock(mLock);
II省略部分內容
snd_pcm_sframes_t n;
size t sent = O;
status t err;
do {
n = snd_pcm_writei(mHandle->handle, (char *)buffer + sent, //调用alsa-lib
snd_pcm_bytes to frames(mHandle->handle, bytes - sent));
if (n == -EBADFD) { //省略部分內容)
else if (n < 0) { //省略部分內容}
else {
mFrameCount += n;
sent += static cast<ssize t>( //荻取PCM的字节敷
snd pcm frames to bytes(mHandle->handle, n));

} while (mHandle->handle && sent < bytes); //判定字节
return sent;

送里主要调用的是 ALSA 庠的 snd_pcm_bytes_to_fram飆)函數, 迸行 Audio 數据流的


写操作。
与上面的情況癸似, AudioStreamlnALSA.cpp 中的 AudioStreamlnALSA::read ()函敷调
用 snd_pcm_bytes_to_frames()和 snd_pcm_readi() 完成了対 Auido 系统流的渡操作。
而没置癸的接口大都通迥 alsa-lib 中以 snd_mixer_为前綴的接口实現。

�7.4.4 MSM平台和Nexus One系统的实瑰


MSM 系统的 Audio 实現, 也用于 Nexus One 等手机, 其特燕是高筒的 QSD 灶理器没
有使用 oss 或者ALSA 等杯准的驱劫架杓, 而是用了 MSM 平台的私有驱劫。

1. Audio 驱劫程序

MSM 平台的 Audio 驱劫程序在 arch/arm/mach-msm/qdspX 目汞中, 迏是因为 MSM 灶


理器的 Audio 系统和其 DSP 灶理器具有密切的美系。 対于使用版本为 5 的 DSP 灶理器
(MSM7k 系列), 其目汞为 qdsp5, 対于使用版本为 6 的 DSP 灶理器 (QSD8k 系列), 其目

, 132
第7章 音頻系统 ooe

呆为 qdsp6; 迏令 Audio 系统和 MSM 灶理器的 DSP 系统是紫密相连的。


染文件方面, include/linux/ 目呆中的 msm_audio.h, qdsp6 的特定失文件为 arch/arm/

mach-msm/include/mach/ 目泵的 msm_qdsp6_audio.h。

Auido 涉及的文件情況如下所示。
• audio_ctl.c: 音颜控制文件, 生成没各节熹/dev/msm_audio_ctl。
• routing.c: 音颜路往控制, 生成没各节燕/dev/msm_audio_route。
• pcm_in.c: PCM 榆入通道, 生成没各节熹/dev/msm_pcm_in。
• pcm_out.c : PCM 榆出通道, 生成没各节煮/dev/msm_pcm_out。
• mp3.c: MP3 碼流直接諭出通道, 生成没各节熹/dev/msm_mp3 。
事实上,MSM 的 Audio 系统使用的是非杯准的驱幼程序,在用户空阅包括两介控制节
燕和三令敖据节燕。 两令控制节燕分別用于控制 Au山o 基本內容和路往, 三介榆出节屯包
括 PCM 的榆出、 PCM 的榆入、 MP3 編碼流的榆出。 MSM 系统比絞特殊的地方在于用户
空冏可以直接使用节煮諭出編碼的 MP3 編碚流, 榆出的編碼流将由 MSM 灶理器內部的
DSP 硬件米灶理。
include/linux/ 目汞中的 msm_audio.h 定乂了 Audio 的 ioctl 控制命令, 內容如下所示:
#define AUDIO IOCTL MAGIC 'a'
#define AUDIO START IOW(AUDIO IOCTL MAGIC, 0, unsigned)
#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
#define AUDIO_SET_CONFIG 一IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
#define AUDIO_ GET_STATS _IOR(AUDIO一IOCTL_MAGIC, 5, unsigned)
#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned)
#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned)
#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned)
#define AUDIO_SET_RX_IIR _IOW(AUDIO一IOCTL_MAGIC, 9, unsigned)
#define AUDIO一SET_VOLUME _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned)
#define AUDIO_ENABLE_AUDPRE _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned)
#define AUDIO_SET_AGC _IOW(AUDIO一IOCTL_MAGIC, 12, unsigned)
#define AUDIO_SET_NS _IOW(AUDIO_IOCTL_MAGIC, 13, unsigned)
#define AUDIO一SET_TX_IIR _row(AUDIO一IOCTL_MAGIC, 14, unsigned)
嵒define AUDIO PAUSE row(AUDIO IOCTL MAGIC, 15, unsigned)
#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO一IOCTL_MAGIC, 30, unsigned)
fdefine AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned)
#define AUDIO_SWITCH_DEVICE _IOW(AUDIO一IOCTL_MAGIC, 32, unsigned)
fdefine AUDIO SET MUTE IOW(AUDIO IOCTL MAGIC, 33, unsigned)
#define AUDIO_UPDATE_ACDB _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned)
fdefine AUDIO_START_VOICE 一IOW(AUDIO一IOCTL_MAGIC, 35, unsigned)
#define AUDIO_STOP_VOICE _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned)
IO_REIN;i;_.T_�DB ..JOW(AUD.1.Q_IOCTI_.i_MAGI.fu..___3�,-洱逵ig珥迢)

以上 ioctl 命令是统 一定叉的, 但是它伯分別在不同没各中的 ioctl 调用中实現。 例如


AUDIO_START、 AUDIO_S TOP、 AUDIO_FLUSH 等流控制命令在敷据通道节燕中使用;
AUDIO_SWITCH_DEVICE、 AUDIO_UPDATE_ACDB 、 AUDIO_REINIT_ACDB 等命令在
msm_audio_ctl 控制没各中使用。 比絞特別的地方在于 msm_audio_route 没各节燕没有 ioctl

命令, 是通這写没各节熹釆完成命令的。

133�
•oo Android 板级支持与硬件相美子系统

2. Audio 硬件抽象居

MSM 系 统的硬 件 抽 象 扆己経包含在 Android 的通用代碼中 , 內容包含在


hardware/msm7k/ 目汞中, libaudio为MSM7k 灶理器的实現 libaudio-qsd8k为QSD8k 灶理
器的实珗。
在 libaudio-qsd8k 目汞中包含以下文件。
e msm_audio.h: 与內核相同的 ioctl 命令和數据結杓的定又。
e AudioHardware.h: Audio 硬件抽象屄的炎定又。
e AudioHardware.cpp: Audio 硬件抽象扆的实瑰。
e AudioPolicyManager.h: Audio 策略管理的奕定叉。
e AudioPolicyManager.cpp: Audio 策略管理的实瑰。
AudioHardware.h� 文 件中 定 又了 AudioHardware 、 AudioStream0utMSM72xx 、
AudioStreaminMSM72xx迏三令英分別继承AudioHardwareBase、 AudioStreamOut 和
AudioStreamln, 实現 Audio 系统恙控、 諭出和榆入坏节。
AudioStreamOutMSM72xx 中实現的 wr泅)函敖如下所示:
ssize_t AudioHardware::AudioStreamOutMSM72xx::write(const void* buffer,
size_t bytes) {
status_t status = NO_INIT;
size_t count = bytes;
canst uintB t* p = static_cast<const uintB_t*>(buffer);
if (mStandby} {
status = ::open("/dev/msm_pcm_out", O_RDWR}; II打玕驱劫程序
//省略部分內容

mFd = status;
struct msm_audio_config config;
status = ioctl(mFd, AUDIO_GET_CONFIG, &config); II荻得配賀
//省略部分內容
config. channel count = AudioSystem::popCount(channels());
config.sample_rate = mSampleRate;
config.buffer_size = mBufferSize;'
config.buffer count = AUDIO睏NUM OUT BUF;
config.codec_type = CODEC_TYPE_PCM;
status = ioctl(mFd, AUDIO_SET_CONFIG, &config); //迸行配蹬
//省略部分內容
uint32_t acdb_id = mHardware->getACDB(MOD_PLAY, mHardware->get snd dev());
status = ioctl(mFd, AUDIO START, &acdb id); //升始Audio系统
//省略部分內容
status = ioctl(mFd, AUDIO SET VOLUME, &stream volume); II没置音攙
//省略部分內容
acquire_wake_lock(PARTIAL_WAKE_LOCK, kOutputWakelockStr);
mStandby = false;

while (count) {
ssize_t written = : :write(mFd, p, count); //対PCM諭出迸行写操作
if (written > = 0) { //计算剩余的數据鶿
count - = written; //剩余的字节數減少written
p + = written; //写的位置增加written
) else {
if (errno ! = EAGAIN) return written;

�134
第7 章 音頻系统 ooe
mRetryCount++;

return bytes;
//省略部分內容

基本灶理的流程为打升 PCM 諭出没各 C/dev/msm_pcm_out), 漾取配置,没置配置(包


括釆祥率、 緩沖匡大小等), 通迥 ioctl 命令玕始 Audio 流, 迸行対文件的写操作。
AudioStreamlnMSM72xx中实珗的rea叭)函數的实瑰主体如下所示:
ssize_t AudioHardware::AudioStreaminMSM72xx::read( void* buffer, ssize_t bytes) {
size_t count = bytes;
uint8_t* p = static_cast<uint8_t*>(buffer);
//省略部分內容
if (mState < AUDIO INPUT STARTED) { //特掞蚩前的狀态机到榆入玕始
mState = AUDIO INPUT STARTED;
mHardware->set_mRecordState(l);
mHardware->clearCurDevice();
mHardware->doRouting();
acquire wake loc-k(PARTIAL WAKE LOCK, kinputWakelockStr);
uint32_t acdb_id = mHardware->getACDB(MOD_REC, mHardware->get snd dev ());
if (ioctl(mFd, AUDIO START, &acdb id)) { II玕始Audio敷据流
standby();
retu:i::n 一 1;

while {count) {
ssize_t bytesRead = ::read(mFd, buffer, count); //対PCM榆出迸行埃操作
if (bytesRead >= 0) { //计算坯需要涙取的敷据量
count -= bytesRead; //剩余的字节敷目減少bytesRead
p += bytesRead; II埃的位笠增加bytesRead
) else {
if (errno != EAGAIN) return bytesRead;
mRetryCount++;

return bytes;

迏里使用的 mFd 是打玕的 Audio PCM 諭入没各 (/dev/msm_pcm_in) 的描述符, 在


AudioStreamlnMSM72xx 的 s叫)接口中, 迏介没各已綬被打玕并迸行了基本的配置。 迏部
分代碼如下所示:
status t AudioHardware::AudioStreaminMSM72xx::set(
AudioHardware* hw, uint32_t devices, int *pFormat, uint32_t *pChannels,
uint32 t *pRate, AudioSystem::audio_in_acoustics acoustic_flags) {
II省略部分內容
mHardware = hw;
II省略部分內容
status t status = ::open("ldevlmsm_pcm_in", O_RDWR}; II打升榆入没各
II省略部分內容
mFd = status;
mBufferSize = hw->getBufferSize(*pRate, AudioSystem::popCount(*pChannels}};
struct msm_audio_config config;
status = ioctl{mFd, AUDIO_GET_CONFIG, &config); II埃取配置
II省略部分內容

135�
•oo Android板级支持与硬件相美子系统

config.channel_count = AudioSystem::popCount(*pChannels);
config.sample_rate = *pRate;
config.buffer_size = mBufferSize;
config.buffer_count = 2;
config.codec_type = CODEC_TYPE_PCM;
status = ioctl(mFd, AUDIO_SET_CONFIG, &config); II写入配置
//省略部分內容: 重新渡入配圏
return NO ERROR;
II省略部分內容

根据上面的情況可以得知, 基本的敖据流操作分別需要通迥 /dev/msm_pcm_out和


/dev/msm_pcm_in没各文件米实現。 主体的功能坯是涙/写没各节熹, 坯需要使用ioctl命令
迸行輔助。
在AudioHardware的杓造函敖中 , 則是打玕MSM的Audio系统的控制节燕, 并迸行
基本操作, 內容如下所示:
int fd = open("/dev/msm_audio_ctl", O_RDWR);
if (fd >= 0) (
ioctl(fd, AUDIO STOP VOICE, NULL); II没置停止语音
close(fd);

AudioHardware的set_mic_muteO函數实瑰的是Auido硬件抽象尼定叉的接口, 其內容
如下所示:
static status_t set_mic_mute(bool _mute){
uint32_t mute = _mute;
int fd = -1;
fd = open("/dev/msm audio ctl", O_RDWR); II打升音颜的控制没各
// .....省略惜课姓理部分內容
if {ioctl(fd, AUDIO_SET_MUTE, &mute)) { II没置靜音
close(fd);
return -1;

close(fd);
return NO_ERROR;

Audio Hardware中的set_volume_rpc()被setVoice Volume()等函數调用, 实現如下所示:


static status_t set_volume_rpc(uint32_t volume) {
int fd = -1;
fd = open("/dev/msm_audio_ctl", O RDWR); //打升音颜的控制没各
//省略錯泯灶理部分內容
volume *= 20; /I音鷽的內容用百分比
if (ioctl(fd, AUDIO一SET_VOLUME, &volume)) { //没置音量

close(fd);
return NO_ERROR;

由此可見, Audio硬件抽象展的控制功能基本上都是通迥操作/dev/msm_audio_ctl没各
节熹米完成的。
MSM的Audio硬件抽象展没置的调用功能为setParameters=>doAudioRouteOrMute=>

, 136
第7章 音頻系统 ooe

do_route_audio_dev_ ctrl, 没各切換方面的功能也在其中实現 。

�7A.5 Nexus S 系统的实瑰

1. Audio驱劫程序
Nexus S的音颜系统使用ALSA实現,实瑰的主要目汞是sound/soc/s3c24xx/,另有 Codec
部 分 的 內 容在 sound/soc/codecs/目杲中 。 其 中的 主 文 件是 soc/s3c24xx/目呆中的
herring-wm8994.c, 此文件生成的目才示是 snd-soc-herring-wm8994.o。
Nexus S的音頻驱劫包括/dev/sound目汞几介没各节燕: controlCO为主控制 没各,次没
各另为O, pcmCODOp为敖据榆出 没各, 次没各另为16, pcmCODOc为敷据榆入没各, 次
没各另为24, timer为定时器 没各, 次没各弓为33。
Nexus S 的音颜驱幼平台没各的名稔为 soc-audio.O, 在sys 文件系统中的路往为:
/sys/devices/platform/soc-audio.0/sound/cardO。
此 音颜驱劫的 Codec 部分另有用于调试寄存器的內容, 査看的片段如下所示:
嵒 cat /sys/devices/platform/soc-audio.0/codec reg
WM8994 registers


0: 8994

其中, 依次列出的是WM8994 Codec芯片的各介寄存器 。

2. Audio硬件抽象扆
Nexus S 系统音颜部分的硬件抽象屄屑于板级部分, 基本上是一 令基于ALSA 驱劫程
序的实現, 其代碼路往为: device/samsung/crespo/libaudio/。
ALSA部分 是一 介 简化的 ALSA用户空伺庠的实現, asound.h是 ALSA杯准的失文件

(与內核部分相同),alsa_audio.h是简化的用户空l 司染文件。
alsa_pcm.c和alsa_mixer.c分別是ALSA部分PCM 敖据通道和混音器控制的实現文件。
由于Nexus S的音颜系统比絞固定, 因此它亻f]是简化音颜系统的实現, 不需要考慮劫态音
颜没各的同題。
alsa_mixer.c实現的打升函數如下所示:
struct mixer *mixer open(void){
struct snd ctl elem list elist;
struct snd_ctl_elem_info tmp;
struct snd ctl elem id *eid = NULL;
struct mixer *mixer = NULL;
unsigned n, m;
int fd;
fd = open("/dev/snd/controlCO", 0 RDWR); // 直接打升固定的音颜没各节煮
if (fd < 0) return O;
memset(&elist, 0, sizeof(elist));
if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0) goto fail; // ioctl没箕
II省略其他內容
return mixer;
II省略鐨课灶理內容

137�
•oo Android 板级支持与硬件相美子系统

rnixer_op画)函敖返回奕型是 mixer 炎型的指針, 表示混音器没各的句柄, 调用者并不


需要知道結杓具体的內容。迸而以其为參數调用 mixer_get_ control()可以荻得 rnixer_ctl 炎型
的指針, 表示混音器没各各秤控制接口。 迸而可以使用 mixer_ctl_ set() 等函敖迸行控制。
alsa_pcm.c 中的打玕函數如下所示:
struct pcm *pcm_open(unsigned flags) {
const char *dname;
struct pcm *pcm;
struct snd_pcm_info info;
struct snd_pcm_hw_params params;
struct snd_pcm_sw_params sparams;
unsigned period_sz;
unsigned period_cnt;
pcm = calloc(l, sizeof(struct pcm)}; II表示ALSA的PCM没各
if (!pcm} return &bad_pcm;
if (flags & PCM_IN} { dname = "/dev/snd/pcmCODOc";) II PCM榆入没各
else { dname = "/dev/snd/pcmCODOp";) II PCM榆出没各
pcm->flags = flags;
pcm->fd = open(dname, O_RDWR);
if (pcm->fd < 0) { return pcm; }
if (ioctl(pcm->fd, SNDRV PCM IOCTL INFO, &info)) { goto fail; } //荻得信息
info_dump(&info); //仅仅用于界出信息
//対snd_pcm_hw_params和snd pcm sw params哭型迸行固定的參數没置
pcm->buffer_size = period_cnt * period_sz;
pcm->underruns = O;
return pcm;

根据參數亻考入的 flag, pcm_ope瑱)函敖将打玕固定的諭入和輸出没各节燕, 其返回癸


型是 pcm 的指針, 表示音颜敖据榆入/榆出没各的句柄。 因此癸型为參數调用 pcm_write()
和 pcm_read()可以迸行音颜原始敷据的写和埃。
AudioHardware.cpp 和 AudioHardware.h 及以上文件 alsa_ *将生成 libaudio.so, 作为音颜
主要硬件抽象尼的实現。 垓庠也镱接了 libaudiointerface.a 和用于控制盔牙部分的 liba2dp.a
送两介靜态庠。送是基于 alsa_pcm.c 和 alsa_mixer.c 的实瑰,继承了 Audio 硬件抽象扆的三
令癸, 核心癸 AudioHardware 是 AudioHardwareBase 的继承者 , 几令主要的成贝为:
sp <AudioStreamOutALSA> rnOutput; II諭出没各
Sor:tedVector: < sp<AudioStr:eaminALSA> > minputs; //榆入没各
Mutex mLock;
struct pcm* mPcm; // ALSA的PCM敷据句柄
struct mixer* mMixer; // ALSA的混音器句柄

实际上, 硬件抽象扆可以支持 一 令諭出没各和多令諭入没各, 实际上元诒硬件抽象扆


'` "
返回的 没各 有多少令,它対皮驱劫的都是同 一介没各。并且在打升 AudioStreamOutALSA
和 AudioStreamlnALSA 时, 只是预先没置參敖填充敖据結杓, 只有在埃写的时候, 才対
ALSA 迸行控制。
諭出没各的 write() 和榆入没各 read() 函敖是核心实瑰, 除了分別调用 pcm_write() 和
pcm_read()迸行音颜流的數据之外, 坯有通迥対屯源管理的 wake_lock 迸行没置的部分。

Nexus S 音颜硬件抽象屄中的另 今還緝是它和屯话部分的交互,在初始化阶段,就佘

, 138
第7章 音頻系统 ooe

打升名稔为"libsecril-client.so"的幼态庠。 在 setModeO、 setVoiceVolume() 等函敖的实現迥程


中, 需要対屯话相美的部分迸行控制,
AudioPolicyManager.cpp 和 AudioPolicyManager.h 将生成 libaudiopolicy.so, 作为音颜策
略硬件抽象扆的实現。 迏里没有特別的实現, 只是使用默从的策略实珗。
除此之外, 坯有几介用于潿试可执行程序, aplay.c 生成用于迸行音颜榆出(播放)的
aplay, arec.c 生成用于迸行音颜榆入(呆制)的 arec, arnix.c 生成用于控制的 arnix。

139�
第8章
视頻疊加榆出系统

8.1 视頻疊加榆出系统概述

Android的视颜疊加榆出系统提供视颜幀敖据的諭出功能。视颜疊加榆出系统利用特殊
的视颜諭出没各将视颜敖据单狓迸行諭出。
视颜疊加榆出系统所依賴的硬件是晁示諭出的控制器(比如LCD控制器) 的多尼机
制。 通常情況下, 主晁示巨用于囹形系统的榆出(通常是RGB颜色空阅), 額外的晁示
巨用于视颜的榆出(通常是YUV颜色空囘)。 主晁示巨和疊加晁示巨通迥硬件的混疊自
劫呈現在屏幕上。
• 视颜疊加榆出系统的驱劫程序通常是晁示諭出驱幼。 视颜疊加榆出系统的硬件抽象屄
是Overlay硬件模玦。 Overlay的含又为疊加在主晁示巨之上的晁示囹扆, 通常也可以翻洋
成"视颜疊加尼"。
视颜疊加榆出系统的本地框架扆在Surface�中, 対本地尼次提供了Overlay的APL
可以供视颜播放器和照相机取景器迸行调用。 视颜疊加諭出系统没有Java扆次的部分, 既
不需要在Java尼亻考諭敖据流, 也不能在Java扆实現控制。
视颜疊加榆出系统的相栄內容如表8-1所示。

表8-1 视頻臺加榆出系统的相失內容
Android 的展次 视颎臺加榆出系统部分 描 述

硬件尼次 晁示控制器的特殊OO尼 视颜OO形将和胆形困扆迸行亞加

操作系统尼 Framebuffer 或者 V4L2 等驱劫 袖立于主晁示巨的单袖没各

本地的硬件抽象扆 Overlay 硬件模坎 包括敖据榆出和控制部分

本地框架扆 Surface 中的 Overlay 部分 封裝成供本地程序调用的接口

视颜系统対上尼仅仅提供了本地扆的接口, 迏些本地尼的接口没有被Android 的杯准


部分调用。 如果需要在系统中使用Overlay, 則需要升友者自己去实現対Overlay系统接口
的调用部分, 迏介调用部分通常是视颜播放器的榆出坏节和照相机系统的硬件抽象尼。
第8章 视颜蠢加輸出系统 ooe

一 一
Overlay部分是Android中 介可造的系统,但是视頻敖据幀单狓迸行榆出的功能 般
都有所实瑰, 可能使用其他的使用方式。

丑提示: Android 4.x已綬没有了Overlay鄣分, 祝類輸出采用其他机制。

8.2 視頻榆出子系统的結枸

�8.2.1 Overlay系统的結杓
Android的Overlay没有Java部分,仅包括 视頻諭出的驱劫程序、硬件抽象扆和本地框
架几介部分,Overlay系统結杓如囹8-1所示。

. . . •. . . . . 一. . . . . . . . , !,. . . . . . . . 一.............」
-····································,
i 视颜播放器榆出
,..........•...........................,
: Camera Hal i
: (调用者) ! (调用者)
Overlay API

SurfaceFlinger
DisplayHardware
'

:::'
,'
c


框 一核
架二 全

,'




� 植分

::::::
移部 �

'.
,



囹8-1 Android的Overlay系统結杓

自下而上,Overlay系统包含以下几令部分。
(1)驱劫程序
通常可以基于Framebuffer或者V4L2等用于晁示的驱劫程序。
(2)硬件抽象扆
e bardware/libhardware/include/hardware/overlay.b: 视颜疊加硬件抽象扆染文件。
视颜疊加昆的硬件抽象扆实現后, 将生成名稔为overlay.<hardware> .so的劫态庠。
(3)Overlay的本地框架代碼
e frameworks/base/include/ui/: Ul庠中Overlay的框架部分的失文件。
e frameworks/base/libs/ui/: UI庠中Overlay的框架部分的源代碼。
Overlay系统框架是其中的 一部分, 主要的內容是!Overlay和Overlay, 源代碼被編诵
成庠 libui.so 。
Overlay框架扆实瑰部分在SurfaceFlingerClient中, 为其中的 一 小部分。
e frameworks/base/include/surfaceflinger/ISurface.b: SurfaceFlingerClient 的失文件,包
括Overlay癸的刨建方法。

141�
•oo Android板级支持与硬件相美子系统

• frameworks/base/services/surfaceflinger/: SurfaceFlinger实瑰的LayerBuffer�。
(4) Overlay的调用者
Overlay与Android中其他需要移植的系统存在巨別, 系统不仅需要实現硬件抽象扆,
坯需要实現调用部分(视颜播放器的视颜諭出 和Camera的视颜预蜀坏节)。 其提供的API
主要在视颜的榆出坏节和照相机的取景器中使用。
在Java扆中, android.view包中的SurfaceView用于推送方式的视颜榆出, 圭某介系统
实現Overlay的硬件抽象扆时, 其上昆的SurfaceView奕中也就具有了迏秤支持。 如果有视
颜榆出, 直接从本地扆迸行數据流的友送, Java扆并不需要対Overlay做出任何灶理。

�8.2.2 本地框架展

1. UI庠中的法文件
Overlay在本地框架部分的內容由U1庠描述。. 首先 IOverlay.h中定叉的!Overlay是表
示Overlay的句柄, 迏是 一 令使用BinderI PC机制的接口。
Overlay.h中定乂的哭是Overlay系统対本地展 部分的API, 包括OverlayRef和Overlay
两今哭, OverlayRef的主要內容如下所示:
class OverlayRef : public LightRefBase<OverlayRef> {
public:
OverlayRef(overlay_handle_t, const sp<IOverlay>&, uint32_t w, uint32_t h,
int32 t f, uint32 t ws, uint32 t hs);
II私有成贝中包括寬、 高和颜色格式等信息, 以及!Overlay的句柄

OverlayRef奕以!Overlay刁思高、颜色格式等信息刨建,并用于刨建真正的接口Overlay
奕。 Overlay定又內容如下所示:
class Overlay : public virtual RefBase {
public:
Overlay(const sp<OverlayRef>& overlayRef); II从overlayRef創建
void destroy(); II梢毀
overlay_handle_t getHandleRef() const; II荻得句柄
status_t dequeueBuffer(overlay_buffer_t* buffer); II阻塞等待Buffer返回
status_t queueBuffer(overlay_buffer_t buffer); II友送Buffer
void* getBufferAddress(overlay_buffer_t buffer); //荻得Buffer的地址
//省略没笠和荻取函敷

Overlay系统的调用者就是通迥Overlay奕対视颜疊加展迸行操作的, 其主要的操作是
荻得內存地址、 友送 Buffer和阻塞等待Buffer返回。

2. Surface的法文件
Overlay 系统使用流程是先得到OverlayRef, 然后再从 OverlayRef得到Overlay。
SurfaceFlingerClient的失文件ISurface.h接口定又了相矢內容, 如下所示:
class !Surface : public !Interface {
class BufferHeap { //省略 J; //表示內存的炎
//第1組接口: 申谘Buffer和没'11. Buffer

�142
第8章 视頻査加輸出系统 ooe
virtual sp<GraphicBuffer> requestBuffer(int bufferidx,
uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0;
virtual status t setBufferCount(int bufferCount) = 0;
//第2組接口:注1毌B�ffer和友送Buffer
virtual status_t registerBuffers(const BufferHeap& buffers) = 0;
virtual void postBuffer(ssize_t offset) = 0; // one-way
virtual void unregisterBuffers() = 0;
//第3組接口: 使用Overlay
virtual sp<OverlayRef> createOverlay( uint32_t w, uint32_t h,
int32 t format, int32 t orientation) = O;
};
``
!Surface 中定叉的內容实际上是 推送內存,, 的接口, 三組接口是并立美系, 前两組
是不使用 Overlay 的欽件灶理方法, 最后 一 組是通迥 Overlay 使用硬件的方法。
第 l 組接口是 requestBufferO和setBufferCountO 两介函數, 使用 GraphicBuffer 作为參
敖炎型, 以某介索引注朋 一介 GraphicBuffer, 没置的是晁示序列中的某 一介。
第 2 組接口是 registerBuffersO、 postBuffer()和unregisterBuffers() 三介函數, 其使用流
程为:先注冊 一介 Buffer 序列,表示指定晁示的內存,然后使用 postBufferO 将每 一 令 Buffer
友送到 Surface 上迸行晁示。
第 3 組接口为 createOverlay() , 将得到可以操作 Overlay 的 OverlayRef。

3. SurfaceFlinger 有夫 Overlay 部分的实瑰

SurfaceFlinger 中的部分代碼实現了 Overlay 系统的中阅屄, 奐賣调用 Overlay 系统的


硬件抽象扆, 并向上尼提供 Overlay 接口。
在 DisplayHardware/DisplayHardware.cpp 文件的 DisplayHardware 突的 1Ill吩函敖中,打
升了 Overlay 的硬件抽象扆实瑰,疝splayHardware癸被SurfaceFlinger 的核心部分作为加速
晁示硬件米使用。

LayerBuffer 是 SurfaceFlinger 中所提供的多秤尼(尼的基奕是 LayerBase)� 中的 秤。
一 一
LayerBuffer::OverlayChannel 实际上就是 IOverlay 的 介实現,可用于建立 令 OverlayRef,
迏段內容在 LayerBuffer::OverlaySource 的枸造函數中实瑰, 如下所示:
overlay_control_device_t* overlay dev = getFlinger()一>getOverlayEngine();
mOverlayDevice = overlay dev; II來自硬件抽象扆的哭型
overlay_t* overlay = overlay_dev->createOverlay(overlay_dev, w, h, format);


overlay_dev->setParameter(overlay_dev, overlay,
VERLAY_DITHER, OVERLAY_ENABLE);
mOverlay = overlay; //为overlay_t癸型
II省略没償寛、 高和颜色格式等信息
mOverlayHandle = overlay->getHandleRef(overlay); II overlay handle_t哭型
sp<OverlayChannel> channel = new OverlayChannel( &layer ); //建立萎
*overlayRef = new OverlayRef(mOverlayHandle, channel, //建立OverlayRef
mWidth, mHeight, mFormat, mWidtnStride, mHeightStride);

在建立 一介 Surface 时,如果參敖中的 flag 同时具有 eFXSurfaceNormal和ePushBuffers


将創建 LayerBuffer。 调用者隨后可以通迥 LayerBuffer 得到 !Surface 接口, 荻得 !Surface 接
口之后, 就可以刨建 OverlayRef, 迸而迸行 Overlay 硬件相美的操作。

143�
• O O Android 板级支持与硬件相美子系统

8.3 视頻社加榆出BSP結枸

�8.3.1 移植的內容
Overlay 底尼与系统框架的接口是硬件抽象尼,因此实現 Overlay 系统需要实現硬件抽
象扆和下面的驱劫程序。
Overlay 系统的驱劫程序 一 般是视颜榆出的驱劫程序,通常可以使用杯准的 Framebuffer
驱劫程序或者 Video for Linux 2 的视颜諭出驱劫程序米实現。根据系统的不同,即使使用
同 一 秤驱劫程序,也有不同的实現方式。
Overlay 系统的硬件抽象扆則使用了 Android 中杯准硬件模玦的接口,送是 一 秤純 C 语
言的接口,基本依靠填充函數指針來实現。 送部分包含數据流接口和控制接口,需要根据
硬件平台的情況,有造拌地实現。

�8.3.2 驱劫程序
视颜疊加諭出系统的驱幼程序 一 般是视颜諭出的驱劫程序。在 Linux 中,视颜諭出杯
准的框架有 Framebuffer 驱劫程序和 Video for Linux 2 的视颜諭出炎型。
Framebuffer 驱 幼 程 序 是直接的 方 式, 实 現 视 颜 諭 出 从 驱 劫 程 序 的 角 度 和 一 般
Framebuffer 驱劫程序奕似。唯 一的巨別在于视颜榆出 通常使用 YUV 格式的颜色空伺,而
用于囹形界面的 Framebuffer 通常使用 RGB 的颜色空冏。
Video for Linux (v412) 是也 Linux 中视頻部分的 一 今杯准框架,主要用于视颜流的輸
入和榆出。 Video for Linux I 首先提供了掇像失视颜榆入的框架,从 Video for Linux 2 版本
玕始提供了视颜諭出的接口。使用迏介视颜榆出的接口,更有利于实現臥列的方式,可以
根据系统的性能情況调整臥列的數目。

�8.3.3 硬件抽象展的內容

1. Overlay 硬件抽象展的接口
Overlay.h 中定又了实瑰 Overlay 硬件抽象尼的杯准方法,实現包括以下部分。
.模玦:癸型为 overlay_module_t, 继承自 hw_module_t。
• 控制没各:奕型为 overlay_control_device_t, 继承自 hw_ device_t。
.敷据没各:奕型为 overlay_data_device_t, 继承自 hw_ device_t。
实現后,按照杯准的模玦和没各注朋方式注冊到系统中。从调用者的角度,4 介 inline
函啟則分別奐靑控制和敖据没各的打玕和美困。
overlay_handle_t 表示句柄,overlay_t 表示 Overlay 的主体結杓,它亻[]的定又如下所示:
typedef const native_handle* overlay_handle_t;
typedef struct overlay t { II表示Overlay的上下文
uint32 t w; '` //寛
uint32 t h; //高
int32_t format; II颜色格式
uint32 t w stride; II 一 行的內容

�144
第8章 视頻蠢加輸出系统 ooe
uint32 t h stride; II 一列的內容
uint32 t reserved[3];
overlay_handle_t const* (*getHandleRef) (struct overlay_t* overlay);
uint32 t reserved procs [ 7 J;
) overlay_t;


overlay_handle_t 是 介內部使用的結杓, 用于保存硬件没各的句柄等。 在使用的迥程
中,需要从 overlay_t 荻取 overlay_handle_t。上尼的使用一 般只迸行結杓体 overlay_handle_t
指針的伟逸, 具体操作在硬件抽象扆中完成。

overlay_buffer_t 表示內存奕型, 实际上就是 void*, 可以表示 玦通用的內存。
overlay_module_t 包含了通用硬件模玦癸型, 它"继承"了 hw_module_t, 但是没有在
結枸体中增加其他成贝, 二者是等价的。
在 overlay.h 的各秤定又中,两令核心的炎是 Overlay 控制没各和 Overlay 敖据没各,它
仞的名稔被定又成以下两令字符串:
#define OVERLAY_HARDWARE_CONTROL "control"
jdef:i,ne OYER珥Y HARDWA邸DATA "data"

overlay_control_device_t 和 overlay_data_device_t 两令敖据結杓分別表示 Overlay 的控


制没各和數据没各, 它亻[]都是以 hw_device_t 奕型作为其第 一成贝。
控制没各 overlay_control_device_t 的定又如下所示:
struct overlay control device t {
struct hw device t common;
int (*get) (struct overlay_control_device_t *dev, int name); //靜态荻取
overlay_t* (*createOverlay) (struct overlay_control_device_t *dev, //刨建
uint32_t w, uint32_t h, int32_t format);
void (*destroyOverlay) (struct overlay control device t *dev, II刪除
overlay_t* overlay);
int (*setPosition) (struct overlay_control_device_t dev, II没置位置
overlay_t* overlay, int x, int y, uint32_t w, uint32_t h);
int (*getPosition) (struct overlay_control_device_t *dev, /I荻取位置
overlay t* overlay, int* x, int* y, uint32_t* w, uint32_t* h);
//省略其他没置和荻取功能

敖据没各 overlay_data_device_t 的定又如下所示:


struct overlay data device t {
struct hw device t common;
int (*initialize) (struct overlay data device t *dev, //初始化
overlay handle t handle);
int ( "' resizeinput) (struct overlay data device t *dev, //调整大小
uint32 t w, uint32 t h);
int (*setCrop) (struct overlay data device t *dev,
uint32_t x, uint32_t y, uint32_t w, uint32一_t h) II没置剪切巨域
int (*getCrop) (struct overlay data device t *dev,
uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) II荻取剪切巨域
int (*setParameter) (struct overlay_data_device_t *dev, II没置參敖
int param, int value);
int (*dequeueBuffer) (struct overlay_data_device_t *dev, II等待臥列內存返回
overlay buffer t *buf);
int (*queueBuffer) (struct overlay data device t *dev, II将內存放入臥列
overlay' buffer
" 't buffer);

145�
•oo Android 板级支持与硬件相美子系统

void* (*getBufferAddress) (struct overlay data device t *dev, //将內存放入臥列


overlay buffer t buffer);
int (*getBufferCount) (struct overlay data device t *dev); //将內存放入臥列
-
.
,
'-
.

在功 能上, overlay_control_device_t 奐贤的 是 初 始 化、 銷毀及 控 制癸的 操 作 ,


overlay_data_device_t 姓理的是晁示內存的諭出等效据操作。 敖据没各的 queueBuffer、
dequeueBuffer 和 getBufferAddress 用于 Overlay 核心的敖据流伟逸功能。
从硬件抽象扆的角度, Overlay 的使用分 成以下几介步驟。
第 一 步: 得到 overlay_t 。 通迥调用控制没各 overlay_control_device_t 的函數指針
createOverlay, 參數为寛、 高和颜色空伺格式, 返回 overlay_t 突型的結杓体。
第二步: 得到 overlay_handle_t。 通迥调用 overlay_t 癸型的結杓体中的 getHandleRef
函敖指針, 得到 overlay_handle_t 奕型的結杓体。
第三步: 初始化數据没各。 调用啟据没各 overlay_data_device_t 的函數指針 initialize,
參數为 overlay_handle_t 癸型的結杓体。 數据没各初始化完成, 表示可以使用。
第四步 : 敖 据 没 各 可 以 使 用后 , 调 用 其中 的 dequeueBuffer 、 queueBuffer 、
getBufferAddress 等迸行敖据流操作, 并调用其他函致迸行没置等。

2. 实瑰 Overlay 硬件抽象扆

Overlay 硬件抽象扆的实瑰就是控制没各和敖据没各岂中函敷指針的实現。通常情況下

控制没各的 createOverlay 完成真正的硬件初始化操作工作, 其他步骕 般都是啟据結杓的
杓建。 其他的各介函效指針完成啟据流和没置工作, 将対皮于対硬件没各的操作。
対于 Overlay 硬件抽象扆的实現取決于硬件及其驱劫程序, 主要是數据没各需要分情
況迸行灶理。
如果使用 Framebuffer 驱幼, 情況通常比絞简单, 使用方式和 Framebuffer 映射到用
户空r司是 一 祥的。 一
般情況 下, 实瑰 getBufferAddress 函數, 返回通迥 mrnap 荻得
Framebuffer 的指針即可。如果没有戏緩沖阅題, dequeueBuffer 和 queueBuffer 两介函數不
需要真正实現。
如果使用 Video For Linux 2 的榆出 驱劫, dequeueBuffer 和 queueBuffer 两介函數与调用
驱劫时主要 ioctl 是 一 致的(分別调用 VIDIOC_QBUF 和 VIDIOC_DQBUF), 直接实瑰即可,
其他初始化工作可以在 initialize 中迸行灶理。 由于视颜敖据臥列的存在, 迏里的灶理內容
晁然比一 般的幀緩沖巨要夏尕 一 些, 也可以迏到更高的性能。
在數据流的姓理近程中, 表示內存的 overlay_buffer_t 原本是一介 void*癸型的指針,
而且 Overlay 系统対上展的接口和硬件抽象扆的接口都使用它。 因此可以在实珗的這程中
将其特化成各秤敖据結杓使用, 只要上下展保持 一 致即可。
対于硬件相美的其他操作, 可以在其同的函數指針中实現, 控制没各和數据没各都具
有的 setPararneter 則可以用于实現自定乂操作。

�8.3.4 视頻榆出的调用者
Overlay 系统的特殊性在于它没有杯准的调用者,如果仅仅实現 Overlay 的硬件抽象扆,
対系统的运行没有任何改変。 Overlay 対系统运行的加速作用在于, 实現之后, 由特定部分

, 146
第8章 视頻擡加輸出系统 ooe

调用, 由此完成利用视颜疊加尼硬件迸行视颜榆出的流程。 使用 Overlay 迸行视颜榆出的



令必然的糸件是: Overlay 的使用者友送的视颜幀必颎是 Overlay 硬件可以支持的格式。
Overlay 系统的调用者只有本地接口, 不能通近 Java 尼调用。 Overlay 系统的上尼调用
者也是本地程序, Overlay 系统的调用者调用的是本地框架扆中的 Overlay 癸, 而不是硬件
抽象扆。 主要的调用者是以下的两介坏节。
國视颜播放器的视颜榆出坏节(完成解碼视颜的榆出)
a Camera 的硬件抽象尼(完成取景器的諭出)
Overlay 系统的调用后实际上在本地屄完成了视颜流亻考諭的工作。 Java 尼依然需要対
Overlay 有所支持, android.view 包中的 SurfaceView 奕就是 Surface 的裁体, 作为照相机或
者视頻播放器的諭出。从中得到 Surface 将被亻夸逸到以上的两今坏节中,如果调用函數創建
Overlay, 将迸行视颜的諭出;如果调用其他函致, 将迸行欬件的视颜流榆出。
対于某令特定平台的实現, Overlay 的硬件尼和的调用者都是特定的实珗,因此上下扆
代碼匹配即可, 并不需要严格淌足某令要求, 各令接口可以根据情況炅活使用。
Stagefright 作为多媒体的引擎, 奐責媒体的播放和呆制, Stage和ght 中的视颜諭出部分
的插件則可以有造抒地支持硬件的榆出方式。
相美的失文件的路往为: frameworks/base/include/media/stage和ght。
VideoRenderer.h 中定又 VideoRenderer 癸, 是视颜渲染部分的接口。
class VideoRenderer {
pub訌c:
virtual -VideoRenderer() {)
virtual void render(const void *data, size_t size, void *platformPrivate) = 0;


令內存的指針及其大小, 由于迏令指針是
VideoRenderer 具有 render() 函數, 榆入是
用于渲染諭出的, 因此指針由 const 修怖, platformPrivate 表示平台特定私有數据的指針。
HardwareAPI.h 中主要定又两今接口, 內容如下所示:
extern android::VideoRenderer *createRenderer(
const android::sp<android::ISurface> &surface,
const char *componentName, OMX COLOR FORMATTYPE colorFormat,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight);
extern android::VideoRenderer *createRendererWithRotation(
const android::sp<android::ISurface> &surface,
const char *componentName, OMX_COLOR_FORMATTYPE colorFormat,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight,
int32 _t rota t;._ionDegrees) ;

createRenderer() 和 createRendererWithRotation() 函敖用于荻得视颜的渲染器(榆出坏


节), 后者比前者多了一 介视颜巨域的旋銬角度。 迏两介函數的參數中的 !Surface 癸型表示
諭出坏节的截体, 坯可以没置晁示巨域和解碼巨域的寛和高。
如果某令系统实瑰了 Stage和ght 插件中的函數, 那么在 Stage伍ght 迸行视颜輸出时,
就可以调用它迸行视颜諭出。 迏令插件的实珗就可以基于 Overlay 米实瑰。

Camera 的硬件抽象扆中,具有 介布永癸型的參敖 (mUseOverlay) 。者其为真的时候,

147�
• O O Android 板级支持与硬件相美子系统

将使用 Overlay 迸行视颜諭出, 上尼也可以通這涙取迏令布氷值判新是否是硬件抽象昆、


是否使用 Overlay。

�8.3.5 使用Overlay的數据流情況
Overlay 系 统 的數据流有不 同的灶理方 式 , getBufferAddress 、 queueBuffer 和
dequeueBuffer 送三令接口是 Overlay 的硬件抽象尼和対上尼的接口都具有的, 它亻[]在下尼
实現, 被上扆调用。
根据驱劫程序实瑰不同, Overlay 系统的使用主要有以下几神方式。
第 一 秤方式: 直接 Framebuffer 映射的方式。
直接 Framebuffer 映射的方式, 实际上就是通迥普通的 Framebuffer 驱劫程序荻取 一 介
玦內存,垓內存通迥 mmap 从內核得到。在 Overlay 的几令扆次中通常使用 getBufferAddress
接口荻得送玦內存的地址, 隹遊給上扆即可。
第二神方式: 戏緩沖 Framebuffer 方式。
戏緩沖 Framebuffer 方式是使用 Framebuffer 驱劫程序的优化方式, 需要 Framebuffer
驱幼程序实珗戏緩沖巨。 在 Overlay 系统的硬件抽象尼中, Framebuffer 驱劫戏緩沖的切換
可以使用 FBIOPUT_VSCREENINFO 或者 FBIOPAN_DISPLAY 迏两令 ioctl 命令來完成,基
本的思路就是调整 y 方向的位置。
Overlay 經典的接口方式是使用 queueBuffer 和 dequeueBuffer 迏两令接口作为戏緩沖巨
的切換接口。 根据 Framebuffer 驱劫的特,亞 queueBuffer 需要切換緩沖巨, dequeueBuffer
一般不需要迸行工作。 晁然, 通這 setParameter 的接口也是可以的。
直接 Framebuffer 映射方式和戏緩沖 Framebuffer 方式的 Overlay 如囹 8-2 所示。
VideoData
O 仃 l

VideoData

Overlay HAL

Overlay HAL
ioctl
(FBIOPAN_DISPLAY)

用户空伺
用户空冏 內核空伺
內核空冏

Framebuffer驱劫

囹8-2 直接 Framebuffer 映射方式(左)和戏緩沖 Framebuffer 方式(右)

�148
第8章 视頻藝加諭出系统 ooe

第三秤方式: V4L2 映射—臥列諭出方式。


迏是效率最高的方式, 通常情況下 V4L2 视颜諭出的驱幼使用的是从內核映射上釆的
內存(対皮 V412 的 V4L2_MEMORY_MM战內存炎型)。 送时可以实珗 getBufferAddress
从 V4L2 的驱幼程序中得到若干玦內存地址, 在视颜流启劫之后, 实瑰 queueBuffer 和
dequeueBuffer, 分別调用 V4L2 的驱劫程序的 ioctl 命令 VIDIOC_QBUF 和 VIDIOC_DQBUF
迸行操作。
使用 V4L2 驱劫映射一臥列榆出方式的 Overlay 如囹 8-3 所示。

VideoData

queueBuffcr
dequeueBuffer
(0-3)

Overlay HAL
vice t

IOctl
(VIDIOC_QBUF,
mmap
VLDIOC_DQBUF)


用户空l 司

內核空伺

V4L2榆出驱劫

囹8-3 使用V4L2驱劫映射-臥列諭出方式

8.4 视頻榆出BSP的实現

�8.4.1 骨架实班
'`
Android 提供了

介 Overlay 硬件抽象扆的 骨架实現", 送是 一 今代碼示例, 也可以
作为 一 令 Overlay 的硬件抽象尼使用, 但是它没有使用具体的硬件, 也不合有实际的昷示
效果。 其代碼在以下的目呆中实現: hardware/libhardware/modules/overlay/。
迏令目汞中只包含 一 介 overlay.cpp 和 一 令 Android.mk 文件, 其中的內容将生成名为
overlay.trout .so 的幼态庠, 将被放置在目杯文件系统的 system/lib/hw 目汞中。
其中只有一 令 overlay.cpp 文件, 首先完成 overlay_module_t 結杓体的定乂, 其实主要

149�
•oo Android 板级支持与硬件相美子系统

的內容也就是 一 介 �w_module_t 癸型的結枸体。


static struct hw_module_methods_t overlay_module_methods = {
open: overlay device open //Overlay的打玕函敷
};
const struct overlay_module_t HAL_MODULE_INFO_SYM = {
common: { //hw module t癸型
tag: HARDWARE MODULE TAG,
version maJor: 1,
version minor: 0,
這: OVERLAY_HARDWARE_MODULE_ID,
name: "Sample Overlay module",
author: "The Android Open Source Project",
methods': &overlay_module_methods,

};

此灶定又的 overlay_control_context_t 和 overlay_data_context_t 送两令敖据結杓用于护


充 Overlay 的控制没各和數据没各, 如下所示:
struct overlay_control_context_t {
struct overlay control_device_t device;
II可以增加私有啟据,护充overlay_control_device t結杓体
};
struct overlay_data_context_t {
struct overlay 'data ' device_t device;
II可以增加私有數据, 护充overlay_data_device t結杓体
};

在杓造实际 Overlay 硬件抽象扆时, 也可以通迥增加更多的成贝米保存私有的內容,


相圭于实現了继承。
迏介模玦的打升函敷 overlay_device_open 的实現如下所示:
static int overlay_device_open(const struct hw_module_t* module, const char* name,
struct hw device t** devi•ce)

int status = -EINVAL;


if (!strcrnp(narne, OVERLAY_HARDWARE_CONTROL)) { //Overlay控制没各
struct overlay_control_context_t *dev;
dev = (overlay_control_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev)); //初始化結杓体
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module= const_cast<hw_module_t*>(module);
dev->device.common.close = overlay_control_close;
//省略没置控制没各的各介函敖指針
*device = &dev->device.common;
status = O;
} else if (! strcmp(name, OVERLAY_HARDWARE_DATA)) { //Overlay敖据没各
struct overlay_data_context_t *dev;
dev = (overlay_data_context_t*)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev)); //初始化結杓体
*device = &dev->device.common;
dev->device.common.close = overlay_data_close;
//省略没置數据没各的各介函敖指針
status = 0;

return status;

�150
第8章视頻藝加輸出系统 ooe

在实际的初始化這程中,所需要做的事情主要是将 Overlay 的控制没各和數据没各的


函數指針没置成自己实現的函敖,上尼在调用 Overlay 系统対外的接口时,最終将调用到
迏些函數。
在 hw_device_t 的結杓中,坯包含了美 l沮函敖的指針,在迏介函數中需要秤放上下文中
所有分配的內容。

�8.4.2 OMAP系统的实瑰
TI 的 OMAP3 平台絞早实現了通迥 Overlay 迸行视颜播放和 Camera 取景器的视颜硬件
榆出。 Overlay 相美的实瑰分成三部分: V4L2 的视颜喻出驱劫程序、硬件抽象扆和 Overlay
的调用者(其中之 一是 Stagefright 的加速插件)。

1. 视頻榆出部分的驱劫程序
OM战平台的视颜諭出驱劫程序由 drivers/media/video/omap-vout 目呆中的文件米实
現,包含以下几介部分。
• omapvout.h 和 omapvout.c: 主框架,注冊 V4L2 榆出驱劫程序的接口。
• omapvout-mem.h 和 omapvout-mem.c: 实現內存映射、分配和秤放等基絀功能。
• omapvout-vbq..h 和 omapvout-vbq.c: 实瑰虛姒內存的操作。
• omapvout-dss.h 和 omapvout-dss.c: DSS 系统功能的封裝。
在 omapvout.c 的 omapvout_probe()函敖中,建立了若干令 Video 榆出没各,迏介函數
的主体如下所示:
static int _init omapvout_probe(struct platform_device *pdev) {
struct omapvout_bp *bp = NULL;
struct omap_vout_config *cfg;
i.nt i;
int re = 0;
static const enum omap_plane planes[]= { //昱示子系统的两介平面。 対皮两介没各
OMAP DSS VIDEOl, OMAP_DSS_VIDE02, };
II省略部分內容
if (cfg->max_width > 2048) cfg->max_width = 2048; II硬件限制
if (cfg->max_height > 2048) cfg->max_height = 2048; //硬件限制
if (cfg->num_buffers > 16) cfg->num_buffers = 16; II強制性限制
if (cfg->num_devices > 2) cfg->num_devices = 2; II硬件限制
for (i = _0; i < cfg->num_devices; i++)
if (cfg->device_ids [i] > 64)
cfg->device_ids[i] = -1;
II省略部分內容
for (J. = O; i < cfg->num_devices; i++) {
re = omapvout_probe_device(cfg, bp, planes[i], II探測Video没各
cfg->device_id.s [i]);
II省略部分內容

return O;

omapvout_probe_deviceQ是实际的初始化函數,其中调用 video_register_device 注冊了

151�
•oo Android 板级支持与硬件相美子系统

V4L2 的视颜諭出没各, 在迏里 cfg->num_devices 的敖目实际上为 2 (实际上 OMAI弓系列


灶理器的 DSS 支持两令 Video 榆出尼), 因此合建立两介 Video 没各。
omapvout_devdata 为 video_device 奕型的結杓, 是 omapvout_probe_deviceO中被注朋的
Video 没各。 迏令結杓如下所示:
static struct video device omapvout_devdata = {
.name = MODULE NAME,
.fops = &omapvout_fops, //V4L2没各的文件操作
.ioctl_ops = &omapvout_ioctl_ops, //V4L2没各的ioctl
.vfl_type = VID ' TYPE OVERLAY I VID TYPE ' CHROMAKEY, //V4L2没各的哭型
.release = video device release,
.minor = -1,
};

事实上,驱劫程序中实現的主要內容是 v4l2_file_operations 奕型的結杓 omapvout_ fops,


v412_ioctl_ops 奕型的結杓 omapvout_ioctl_ops 中的各介函敖指針。
在用户空冏中, 其没各节燕通常是/dev/video l和/dev/video2, 主没各另为 81, 次没各
另分別是 l和 2 。 迏两介没各対皮了各自狓立的相同硬件, 因此其使用的驱幼程序的代碼
也是基本相同的。

2. Overlay 硬件抽象展

OM 战平台的 Android 系统实現了 Overlay 硬件抽象扆, 迏介硬件抽象扆是基于 V4L2


的视颜榆出驱幼程序米实瑰的。
OM战的 Overlay 硬件抽象扆的实現路往为: hardware/ti/omap3/liboverlay 。
Android.mk 文件中定叉了生成的劫态庠为 overlay.omap3.so, 放置在目柝系统 的
/system/lib/hw 中, 迏是 Android 中杯准的硬件模坎。
overlay.cpp 提供了 OMAP 平台 Overlay 硬件模玦的框架, v412_utils.h 和 v412_utils.c 提
供了対 V4L2 驱劫程序调用的封裝。
overlay.cpp 中, overlay_createOverlayO函致是 Overlay 控制没各的 createOverlay 函數指
針的实現, 迏介函數的主体內容如下所示:
static overlay_t* overlay_createOverlay(struct overlay_control_device_t *dev,
uint32_t w, uint32_t h, int32_t format) {
II省略部分內容
overlay object *overlay;
overlay_control_context_t *ctx = (overlay_control_context_t *)dev;
overlay shared t *shared;
int ret;
uint32 t num = NUM OVERLAY BUFFERS REQUESTED;
int fd;
int shared fd;
II省略部分內容
shared fd = create_shared_data(&shared); II刨建內存的共享巨域
//省略部分內容
fd = v412 overlay_open(V4L2_0VERLAY_PLANE_VIDE01); //打升Overlay没各
II省略部分內容
if (v412_overlay_init(fd, w, h, format)) { goto errorl; } II初始化Overlay
if (v412_overlay_set_crop(fd, 0, 0, w, h)) { goto errorl; } II没箕剪裁巨域
if (v412_overlay_set_rotation(fd, 0, 0)) { goto errorl; } II没置旋特

�152
第8章 视頻聲加諭出系统 ooe
if (v412 overlay req buf(fd, &num, 0)) { goto errorl; } //申消內存
overlay = new overlay_object(fd, shared_fd, shared->size, w, h, format, num);
II省略部分內容
ctx->overlay "videol = overlay; II上下文的姓理
overlay->setShared(shared);
shared->controlReady = 0;
shared->strearnEn = O;
shared->streamingReset = O;
shared->dispW = LCD_WIDTH; II没置寬度
shared->dispH = LCD_HEIGHT; II没置高度
II省略部分內容
return overlay;
II省略部分內容

v412_overlay_ope瑱)用于打升 Overlay 没各, 參數是 Ovelray 的没各弓, 在 v412_utils.c


中实瑰, 內容如下所示:
int v412_overlay_open(int id)

if {id == V4L2_0VERLAY_PLANE_VIDE01)
return open("/dev/videol", O_RDWR}; //打升第1介Video榆出没各
else if (id == V4L2_0VERLAY_PLANE_VIDE02}
return open("/dev/video2", O_RDWR}; //打升第2介Video榆出没各
return -EINVAL;

由此可見, Overlay 没各包含了 /dev/videol 和 /dev/video2 送两介没各,在硬件抽象昆的


实瑰中, 先打升使用第 1 介, 如果有需要, 再打升使用第二介。
v4l2_utils.c 中的函數是対 V4L2 驱劫程序的封裝, v412_overlay_map_bufO在初始化阶
段定又, 主要功能是从驱劫没各中得到內存。
int v412_overlay_map_buf(int fd, int index, vc.id **start, size_t *len)

II省略部分內容
struct v412_buffer buf;
int ret;
ret = v412 overlay_query_buffer(fd, index, &buf); //首先査洵信息
II省略部分內容
*len = buf. length;
*start = mmap(NULL, buf.length, PROT_READ I PROT WRITE, MAP SHARED,
fd, buf.m.offset); II映射內存
II省略部分內容
return O;

v4l2_overlay_query_bufferO函數调用了 V4L2 的 ioctl 命令査洵內存, 其內容如下所示:


int v412_overlay_query_buffer(int fd, int index, struct v412_buffer *buf)

//省略部分內容
memset(buf, 0, sizeof(struct v412_buffer));
buf->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; // Buffer的哭型为Video榆出
buf->memory = V4L2_MEMORY_MMAP; //內存奕型为从內核得到的映射內存
buf->index = index;
II省略部分內容
retui;n v412_overlay_ioctl(fd, VIDIO巳_QUERYBUF, buf, "querybuf ioctl");

153�
•oo Android 板级支持与硬件相美子系统

經述VIDIOC_QUERYBUF 命令调用后, 将得到填充所得的 v4l2_buffer 結杓, 从指針


中返回。
v4l2_overlay_stream_on() 和 v412_overlay_stream_off() 也是重燕函數,用于敷据流的打升
和美朗, 分別调用了 v4L2 的 ioctl 命令 VIDIOC_ STREAMON 和 VIDIOC_ STREAMOFF 。
v4 l2_overlay_ q_buf() 和 v412_overlay_dq_buf() 是 Overlay 在使用迥程中的重煮, 対皮
Overlay 敷据没各的 queueBuffer 和 dequeueBuffer 迏两令接口。
v412_overlay_q_buf 和 v412_overlay_dq_buf 迏两介函數的实現如下所示:
int v412_overlay_q_buf乜nt fd, int inclex) {
struct v412_buffer buf;
int ret;
but.type = V4L2 BUF TYPE VIDEO OUTPUT; // Buffer的哭型为V這eo榆出
but.index = index;
but.memory = V4L2_MEMORY_MMAP; //內存炎型为从內核得到的映射內存
but.field = V4L2 FIELD NONE;
buf.tirnestamp.tv_sec = 0;
buf.timestarnp.tv_usec = 0;
but.flags = O;
return v412_overlay_ioctl(fd, VIDIOC_QBUF, &buf, "qbuf"); //迸行端口控制操作

int v412_overlay_dq_buf(int fd, int *index) {


struct v412_buffer buf;
int ret;
buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; // Buffer的突型为Video榆出
buf.memory = V4L2_MEMORY_MMAP; //內存癸型为从內核得到的映射內存
ret = v412 overlay ioctl(fd, VIDIOC DQBUF, &buf, "dqbuf"); //迸行端口控制操作
//省略部分內容
*index = buf.index;
return 0;

在迸行內存臥列操作时,使用的是 V4L2_MEMORY_MMAP 突型的內存,一般情況下,


V4L2 作为视颜愉出时, 均使用迏秤炎型的內存。 迏是从內核空岡中映射上米的內存, 不是
在用户空伺申清的內存。

3. Overlay 的调用者

在 Android 的升源代碼中, 包含了 OMAP 系统为 Stagefright 枸建的 OpenMax 和视颜


榆出插件, 其內容在以下目汞中: hardware/ti/omap3/libstage伍ghthw/, 其中将生成名稔为
libstagefrighthw.so 的劫态庠, 也是 Stage和ght 杯准插件劫态庠的名稔。
视颜諭出部分的相美文件如下。
e stagefright_overlay_output.cpp: 调用 createRender叫)接口。
e TIBardwareRenderer.*: 视頻渲染器的实現。
stagefright_overlay_output.cpp 中定又了 createRenderer()的实瑰, 函敖內容如下所示:
VideoRenderer *createRenderer(const sp<ISurface> &surface,
const char *componentName, OMX_COLOR_FORMATTYPE colorFormat,
size_t displayWidth, size_t displayHeight,
size_t decodedWidth, size_t decodedHeight) (
using android::TIHardwareRenderer;

�154
第8章 视颜擡加輸出系统 oo•
TIHardwareRenderer *renderer � new TIHardwareRenderer(surface,
displayWidth, displayHeight, decodedWidth, decodedHeight, colorFormat);
//省略錯课灶理的內容
return renderer;

createRenderer() 函敷的內容实际上是建立了 一 介 TIHardwareRenderer 奕, 并将其指針


作为 VideoRenderer 癸型的指針返回。
TIHardwareRenderer.cpp 中杓造函數的片段如下所示:

sp<OverlayRef> ref = mISurface->createOverlay(


mDecodedWidth, mDecodedHeight, OVERLAY_FORMAT_CbYCrY_422_I, 0);
II省略錯淚灶理的內容
mOverlay = new Over
, lay(ref);
mOverlay->setParameter(CACHEABLE_BUFFERS, 0);
for (size_t i = O; i < (size_t)mOverlay->getBufferCount(); ++i) {
mapping data t *data =
(mapping_data_t *)mOverlay->getBufferAddress((void *)i); II荻得地址
mOverlayAddresses.push(data->ptr);

minitCheck = OK;

迏些內容实际上是 Overlay 系统的初始化工作。具体接口的调用內容和 Overlay 硬件抽


象扆的实瑰相美。
TIHardwareRenderer 奕的 render 函數的內容如下所示:
void TIHardwareRenderer::render(
const vo這*data, size_t size, vo這*platformPrivate) (
if (mColorFormat == OMX COLOR FormatYUV420Planar) (
convertYuv420ToYuv422( mDecodedWidth, mDecodedHeight, II特掞颜色格式
data, mOverlayAddresses[mindex});
} else {
CHECK EQ(mColorFormat, OMX COLOR FormatCbYCrY);
memcpy(mOverlayAddresses[mindex], data, size); //迸行內存及制

if {mOverlay->queueBuffer{{void *)mindex) == ALL_BUFFERS_FLUSHED) {


misFirstFrame = true;
if {mOverlay->queueBuffer((void *)mindex) ! = 0) { return; } //送入臥列

if (++mlndex == mOverlayAddresses.size()) {
mlndex = 0;

overlay_buffer_t overlay_buffer;
if (I.misFirstFrame) {
status t err = mOverlay->dequeueBuffer{&overlay buffer}; II等待榆出完成
if {err == ALL_BUFFERS_FLUSHED} { misFirstFrame = true; J
else { return;)
) else {
misFirstFrame = false;

迏里使用的也是 Overlay 系统的基本调用迥程。 主要调用 Overlay 的 queueBufferO和


dequeueBuffi叫)函數米完成相皮的工作。

155�
• O O Android 板级支持与硬件相美子系统

�8.4.3 Nexus S系统的实瑰


Nexus S 的 Overlay 实現分成三介部分: V4L2 的视頻諭出驱幼程序、 硬件抽象展和
Overlay 的调用者。 三星処理器也具有多介 FrameBuffer 驱劫程序, 实际使用的是 V4L2 的
视颜諭出驱劫程序。

1. 驱劫程序
三星灶理器的视颜驱劫程序提供 V4L2 视颜榆出的没各。 其中的內容是 Samsung

Camera Interface 驱幼的 部分。
在 /dev/没各目呆中具有 videol 没各节,亞是 V4L2 的视颜榆出没各, 用于视颜榆出。 在
sys 文 件 系 统的 /sys/class/video4linux/ 目 呆 中 , 包括各令 V4L2 视 颜 的 驱 劫, 其 中
s3c-func<number>为名靠 0 和 2 是照相机没各, 1 是视颜諭出没各。
三星灶理器的平台移植文件 arch/arm/plat-s5p/devs.c 中具有 platform_device 的定又,名
稔为"s3c-fimc" o

三星灶理器的失文件 include/linux/videodev2_samsung.h 中則定又三星特定的 V4L2 相


芙的定又。 视颜諭出部分的驱幼程序在 drivers/media/video/目汞中, 除了通用的內容之外,
其中的 samsung/fimc/目呆提供了三星灶理器 V4L2 视颜榆出的驱劫程序。
a fimc_dev.c: Video For Linux 2 的没各注冊。
a fimc_v412.c: 实現 V4L2 中 f司尼的操作 (v412_ioctl_ops) 。
a fimc_output.c: 视颜諭出没各的主要实現。
a fimc_overlay.c: V4L2 中 Overlay 操作的几介实現。
a fimc_capture.c: 视颜捕荻没各的实瑰。
a fimc_regs.c: FIMC 部分的硬件寄存器操作, 提供 f血c_hwset_:XXX()等函數。
由于本部分的內容是视颜捕荻和视颜諭出共用的, 因此 fimc_v412.c 的实現中需要判新
癸型, 然后迸行正确的调用。
static int firnc_qbuf(struct file *filp, void *fh, struct v412_buffer *b) {
struct firnc_control *ctrl = ((struct firnc_prv_data *)fh)->ctrl;
int ret = -1;
if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = firnc_qbuf_capture (fh, b); //在firnc_capture.c文件中
) else if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
ret = firnc qbuf output(fh, b); //在firnc_output.c文件中
) else { //省略措课灶理)
return ret;

対于视颜諭出没各,其主文件是 fimc_output.c, 其中调用了 fimc_hwXXX() 等函敖迸行


了实际的寄存器操作。

2. Overlay 硬件抽象扆

Nexus S 的 Overlay 的硬件抽象扆路往为: device/samsung/crespo/liboverlay/, overlay.cpp


是主入口, v412_utils. *提供了対 V4L2 驱幼程序调用的封裝。生成的內容位于目杯文件系统
/system/lib/hw/ 目呆中 overlay.s5pc 1 1 O.so 劫态庠。

, 156
第8章 视颜蠢加輸出系统 oo•

overlay.cpp 中定又了 overlay_object結枸, 实际上是overlay_t 的继承者, 而overlay_


control_context和overlay_data_context_t則分別是控制没各和敖据没各的继承者。
overlay_createOverlayO函敖即控制没各的createOverlay函敖指針, 它奐靑創建敖据没
各, 其中包含硬件抽象尼初始化的主要部分。 调用了 get_fb_var_screeninfoO函數通迥访祠
Framebuffer驱幼程序荻得晁示屏的尺寸,対于Nexus S系统, 其晁示屏的尺寸 是480x640。
v412_util
s.c 中的 v412
_overlay_openO函數打玕了/dev/video1没各 。 用于友送內存的
v412_overlay_q_b礦)函的实瑰
數 如下所示:
int v412_overlay_q_buf(int fd, int buffer, int zerocopy) {
struct v412_buffer buf;
int ret;
if (zerocopy) {
uint8_t *pPhyYAddr; // Y平面的地址
uint8_t*pPhyCAddr; // CbCr平面的地址
struct fimc buf fimc src buf;
uint8_t index;
memcpy(&pPhyYAddr, (void*) buffer, sizeof(pPhyYAddr)); //迸行內存及制
memcpy(&pPhyCAddr, (void*)(buffer+ sizeof(pPhyYAddr)), sizeof(pPhyCAddr));
memcpy(&index, (void*)(buffer+ sizeof(pPhyYAddr) + sizeof(pPhyCAddr)),
sizeof(index));
firnc_src_buf.base[O] = (drna_addr_t) pPhyYAddr; //荻取Y和CbCr平面的地址
fimc_src_buf.base[l] = (dma_addr_t) pPhyCAddr;
fimc_src_buf.base[2] =(dma_addr_t)(pPhyCAddr + (pPhyCAddr-pPhyYAdd可I4);
but.index = index;
buf.memory = V4L2 MEMORY USERPTR; If使用用户空冏指針方式
buf.m.userptr = (unsigned long)&fimc_src_buf;
buf.length = O;
J else {
buf.index = buffer;
buf.memory = V4L2 MEMORY MMAP; //使用內存映射方式

buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; //为v412_buffer結杓賦值


buf.field = V4L2 FIELD NONE;
buf.timestamp.tv_sec = O;
buf.timestamp.tv_usec = 0;
buf.flags = O;
return v412_overlay_ioctl(fd, VIDIOC_QBUF, &buf, "qbuf"); //调用ioctl访祠硬件

參敷zerocopy 控制Overlay的实瑰使用用户空冏的內存或者 內存映射方式。 在申清內


存时也亻考入同祥的參數。 其中的遷緝为, 默从使用的是內核中的內存映射Czerocopy值为
false), 但是如果颜色格式不被支持,則只能使用用户空冏的內存 。

3. Overlay的调用者
Nexus S的Overlay系统被Stagefright的视颜諭出插件和Camera的硬件抽象扆所调用 。
其中 Stage和ght的视颜榆出插件的代碼路往为: device/samsung/crespo/libstagefrighthw/。
SecHardwareRenderer.h和SecHardwareRenderer.cpp 是视颜渲染器VideoRenderer的具
体实瑰, stage和ght_overlay_ output.cpp中則实瑰了createRendererWithRotation()函數。
视颜渲染器的初始化迥程的核心坏节如下所示:
hf defined (USE_ZERO_COPY)

157�
•oo Android 板级支持与硬件相美子系统

if (fromHardwareDecoder) {
ref = mISurface->createOverlay(mDecodedWidth, mDecodedHeight,
HAL PIXEL FORMAT CUSTOM YCbCr 420 SP, orientation);
mCustomFormat = true;

#else
else {ref = mISurface->createOverlay(mDecodedWidth, mDecodedHeight,
HAL PIXEL FORMAT YCbCr 420 P, orientation); //另 一 神格式

i/endif
mOverlay = new Overlay(ref);
mOverlay->setParameter(CACHEABLE_BUFFERS, 0);
mNumBuf = mOverlay->getBufferCount(); // mNumBuf表示內存的致目

在实現的近程中, 将根据是否迸行內存夏制來創建 Overlay, 根据 USE_ZERO_COPY


宏的情況, 将建立不同格式的 Overlay。
使用 Overlay 迸行渲染的核心坏节如下所示:
void SecHardwareRenderer::render(
const void *data, size_t size, vo這*platformPrivate) {
if (mOverlay.get() == NULL) { return; }
if (mCustomFormat) { //自定乂颜色格式的姓理
overlay_buffer_t dst = (uint8 _ t *)mMemoryHeap->getBase() + mindex*mFrameSize;
if (mColorFormat == OMX COLOR FormatYUV420Planar I I
mColorFormat == OMX COLOR FormatYUV420SemiPlanar) {
handleYUV420Planar(data, size); II YUV420 Planar特殊的颜色灶理

if (mOverlay->queueBuffer((void *} mindex) == ALL_BUFFERS_FLUSHED) {


misFirstFrame = true;
if (mOverlay->queueBuffer((void *)mindex) != 0) {return; } //友送內存

if (++mindex == mNurnBuf) { mindex = 0; } //重咒Buffer敖目
overlay_buffer_t overlay_buffer;
if (!misFirstFrame) {
status_t err = mOverlay->dequeueBuffer(&overlay_buffer); //等待取回內存
if (err == ALL_BUFFERS_FLUSHED) { misFirstFrame = true; }
else {return; }
} else {misFirstFrame = false; }
l else { //非自定又颜色格式的姓理
if (mColorFormat == OMX COLOR FormatYUV420Planar ) {//特殊格式需要內存復制
memcpy(mOverlayAddresses[mindex], data, size);

II省略, 以下为没送內存的姓理, 与上面的情況相同

渲染即友送并取回內存的迥程, 支持 YUV 420 Planar 和 YUV 420 Semi Planar 两秤颜


色格式。 每次调用 queueBuffer()迸行內存笈送, 然后调用 dequeueBuffer()取回內存, 使用
參敖 mlndex 杯记內存的索引。 迏两介迥程是连维的, 后者可能被阻塞。除此之外, 坯需要
判斷岂前是否为第 一 幀, 米确定是否友送。

�158
第9章
照相机系统

9.1 照相机系统概述
Android 的照相机系统提供照相机相美的功能。照相机提供了与擬像失相美的功能,主
要包括取景器、 视颜呆制和拍掇相片几令方面。
照相机系统的硬件没各就是掇像染硬件,为系统提供视颜幀的輸入和拍掇照片的功能。

Linux 中掇像染的驱劫程序是 V4L2 驱劫, Android 中也可以配合其他驱劫 起使用。 照相
机系统的硬件抽象扆是 C十十形式的 Camera 接口。
本地框架扆中照相机服各和照相机的客户端, 分別提供了服各的实現和提供給本地屄

次使用的接口。 方面, 照相机的本地接口可以供掇像机等需要视颜敷据的模玦调用, 另

方面, 照相机的本地接口經迥 JNI 封裝, 提供了対 Java 扆 API 的支持。 Java 扆照相机部
分通迥单 一 奕提供功能, 基本上是本地尼功能的封裝, 但敖据流的姓理有巨別。
照相机系统的相美內容如表 9-1 所示。

表9-1 照相机系统的相矢內容
Android 的展次 音頻系统部分 描 述

硬件展次 掇亻象3 提供视颜致据

操作系统扆 V4L2 字符没各或者其他 可以定又額外的接口

本地的硬件抽象扆 C丑形式的 Camera 接口 主要包括取某器、 视颜汞制和拍掇相片

本地框架扆 CameraServ,ce 、 Camera 客户端 使用 Bmder IPC 机制, 提供本地接口

Java 框架扆 Camera突 可用于杓建照相机、 扭描癸的皮用等


Java 長的 AP! Java 框架扆的 Camera 炎

照相机系统的重意屄次在本地部分, 主要包括取景器、 视頻汞制、 拍掇相片和參敖没


置方面的功能。 照相机的本地接口不仅供 Java 尼通迥 JNI 调用, 也可以給本地的程序直接
调用。 在理讠令上照相机荻取的视颜啟据都可以伟送到 Java 扆, 但通常迏些數据不需要亻考逸
到 Java 扆。仅有少數情況需要在 Java 尼荻取视颜敷据流,例如, 通迥掇像失迸行扭面讠只別
• O O Android板级支持与硬件相美子系统


时, 需要取景器的啟据幀。 视颜敖据 般是YlN格式。

9.2 照相机子系统的結枸

�9.2.1 照相机系统的結枸
Android的Camera系统結杓如囹9-1 所示。

Java皮用

Java Camera Class


(androidhardware.camera)
Java框架

MediaRecorder

Camera API

Camera Service

庫II岫繭m庫血
Camera / !Camera
/ ICameraService

d程架

內核空伺

囹9-1 Android的Camera系统結杓

Camera系统分成了以下几介部分。
(I)驱劫程序
掇像失的驱劫程序通常基于Linux的Video for Linux视颜驱幼框架。
(2) Camera硬件抽象扆
a frameworks/base/include/camera/CameraHardwarelnterface.h。
各介系统根据实現其中的突, 生成libcamera.so 庠。
(3)本地框架扆
a frameworks/base/include/camera/: Camera框架庠失文件。
a frameworks/base/libs/camera/: Camera框架庠源代碼。
Camera系统是其中的一部分, 其 內容以"Camera"为升染的几介文件, 迏部分內容被
編诵成libcamera_client.so。
a frameworks/base/services/camera/libcameraservice/: Camera服各部分。
Camera服各是Android系统中 一 介单狹部分, 通迥调用Camera硬件抽象尼米实現。

, 160
第9章 照相机系统 oo•

(4) JNI 部分
e frameworks/base/core/jni/android_hardware_Camera.cpp。
提供給 Java 炎的本地支持, 也包含反向调用 Java 伟遊信息和數据功能。
(5) Camera 系统的 Java 奕
e frameworks/base/core/java/android/hardware/Camera.java。
Camera 対 Java 扆次的突为 android.hardware.Camera, 自Java 皮用程序尼提供接口。可
用于在 Java 皮用程序扆杓建照相机和扭描炎的程序。

— 提示: 从 Android 2.2 升始, Camera 的庠分成了服夯和客户端部分;从 Android 4.0


部分升始, Camera 硬件抽象屋使用模決形式。

�9.2.2 Camera 的本地展

1. Camera 客户端庠的結枸

Camera 本地部分, 主要包括以下 些失文件。

e Camera.h: 定又Camera 対本地尼的统 接口炎 Camera。
a ICamera.h: 定叉Camera 服各屄的需要实現的接口。
a ICameraService.h: 定又Camera 服各接口。
a ICameraClient.h: 定又Camera 的回调奕。
ICameraService.h定乂了ICameraService 癸, 函數如下所示:
virtual int32 t getNumberOfCameras() = 0;
virtual status_t getCamerainfo(int camerald,
struct Camerainfo* cameralnfo) = O;
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient ,
int cameraid) = 0;

conne頃)函數用于荻得某介掇像失的控制句柄, 以 !Camera 炎型返回。 由于可以支持


多介掇像�(后置和前置), 将其表示为 conne呎)函數的整型參數 camerald。
!Camera 中的几令主要的函數如下所示:
virtual status_t setPreviewDisplay(canst sp<ISurface>& surface) = 0;
virtual status t startPreview() = 0; //预典方面
virtual void stopPreview () = 0;
virtual bool previewEnabled () = 0;
virtual status_t startRecording(} = O; II视颜汞制方面
virtual void stopRecording(} = 0;
virtual bool recordingEnabled(} = 0;
virtual void releaseRecordingFrame(const sp<IMemory>& mem} = 0;
virtual status t takePicture(} -=.. 0; //拍擬照片方面

在照相机使用迥程中, startPreview()、startRecording() 等函敖用于控制, 在预篦迥程中


需要为其没置一 令晁示的諭出没各, 即參數中的 !Surface。
!Camera、ICameraService 和 ICameraClient 几介奕是 IPC 的实珗方式。 Camera 灶于比
絞上展的部分, 是照相机系统本地尼次的统 一 接口。 Camera 炎的函數內容和 !Camera 比絞

161�
•oo Android 板级支持与硬件相美子系统

癸似, 通近 ICarnera 等几令奕迸行实瑰。 Camera 奕中具有两令 setPreviewDisplay() 函敖,

分別以 !Surface和Surface 为參敖癸型。


从迸程矢系上,照相机的服各部分位于 ICarneraService 的实現者的迸程,而调用者(客
户端)部分位于各今皮用程序迸程。

2. CameraService 的实班
CameraService 是照相机系统的中向扆, 対皮硬件抽象扆, 实 瑰了照相机框架中的
ICarneraService和!Camera 。 CarneraService 呈然主要是上下尼之阅 一 介洩扆次的辻渡, 但
其主要灶理的還緝是通迥回调机制迸行的敖据流伟逸。 CarneraService 是照相机系统的公共
部分, 运行于 rnediaserver 本地迸程。
在 CarneraService 的 Android.rnk 文件中如下所示:
ifeq ($(USE_CAMERA_STUB), true)
LOCAL STATIC LIBRARIES += libcamerastub
LOCAL_CFLAGS += -include CameraHardwareStub. h
else
LOCAL SHARED LIBRARIES += libcamera
endif
include $ (BUILD SHARED LIBRARY)

Camera 服各部分被編诵成名为 libcameraservice.so 的幼态庠。蚩編诵宏 USE_CAMERA


_STUB 为 false 时, CameraService 镱接 libcamera.so 劫态庠, 使用实际的 Camera 硬件抽象
尼;者編诵宏 USE_CAMERA_STUB 为 true 时,CameraService 连接 libcamerastub.a 靜态庠,
即使用 Camera 硬件抽象尼的"粧,, 实現。
预竟是 CameraService 需要灶理的最重要冏題, 首先使用布你変量対 Camera 硬件抽象
尼是否使用 Overlay 系统做了巨分:
mUseOverlay = mHardware->useOverlay() ; _ j Isp<CameraHardwareipterf?J,Gs!> mHardw_are;

mUseOverlay 用于巨分 Camera 硬件抽象展的功能。 根据 CameraService 的实瑰, 迏介


值只能在初始化时被埃取 一 次。 作为 一 介 Camera 硬件抽象扆的实現, useOverlay()的返回
值或者是 false 或者是 true, 不能劫态改変。
setPreviewDisplay() 函數用于预萸功能的初始化, 主要部分如下所示:
status t CameraService::Client::setPreviewDisplay{const sp<ISurface>& surface){
II省略部分內容
Mutex: : Autolock lock(mLock);
status t result = checkPid();
II省略部分內容
Mutex::Autolock surfaceLock(mSurfaceLock);
result = NO ERROR;
if (surface->asBinder() ! = mSurface->asBinder()) {
if (mSurface != 0) { / I省略部分內容 )
mSurface = surface;
mOverlayRef = O;
if (mHardware->previewEnabled()) {
if (mUseOverlay) { //判斷是否使用Overlay
result = setOverlay(); //情況1: 没置Overlay
-
} else if (mSurface
- ' != 0) {

�162
第9章 照相机系统 oo•

result = registerPreviewBuffers(); //悄況2: 没置注朋Preview

return result;

Preview 的初始化工作対硬件抽象尼是否使用 Overlay 的情況做出了巨分: 如果使用


Overlay 則把 Overlay 没置下去; 如果没有使用則在 !Surface 的接口中注冊內存。
setOverlay() 函數的主要內容如下所示:
status_t CameraService: : Client::setOverlay() {
int w, h;
CameraParameters params(mHardware->getParameters());
params. getPreviewSize(&w, &h); //从參數中得到寃、 高信息
//省略緒课灶理部分內容
status t ret = NO ERROR;
if (mSurface != 0) {
if (mOverlayRef.get() == NULL) {
for 乜nt retry = O; retry < 50; ++retry) {
mOverlayRef = mSurface->createOverlay(w, h, //創建突OverlayRef
OVERLAY_FORMAT_DEFAULT, mOrientation);
if (mOverlayRef != NULL) break;
usleep(20000);

//省略續课灶理部分內容
ret = mHardware->setOverlay(new Overlay(mOverlayRef)); //没置到HAL

} else {
ret = mHardware->setOverlay(NULL); II没翌空的Overlay, 辻:HAL自行灶理

II省略錯课灶理部分內容
mOverlayW = w;
mOverlayH = h;
return ret;

setOverlay() 函敷从 !Surface 中刨建 OverlayRef, 刨建后建立 Overlay 炎,没置給 Camera


硬件抽象扆。 送是在 Camera 硬件抽象尼使用 Overlay 时预莫的初始化工作。
registerPreviewBuffers() 是美于预寛的初始化工作另外 一 介分支(没有使用 Overlay 时)
的灶理, 函數的內容如下所示:
status_t CameraService::Client::registerPreviewBuffers() {
int w, h;
CameraParameters params(mHardware->getParameters());
params.getPreviewSize(&w, &h);
ISurface: :BufferHeap buffers(w, h, w, h, // 建立BufferHeap
HAL PIXEL FORMAT YCrCb 420 SP, //使用YUV420sp格式
mOrientation, 0, mHardware->getPreviewHeap()); //从HAL荻得內存堆
status t ret = mSurface->registerBuffers(buffers); //向ISurface中注朋Buffer
//省略錯课灶理部分內容
return ret;

registerPreviewBuffers()中调用的是 !Surface 中与 Overlay 元美的接口, 向其中注冊了


Buffer。 Buffer 的內容米自 Camera 硬件抽象尼中得到的內存堆。

163�
•O o Android 板级支持与硬件相美子系统

setPreviewCallbackFlag 函敖的主要片段如下所示:
if(mUseOverlay) {
if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
mHardware->enableMsgType(CAMERA MSG PREVIEW FRAME); //使能 preview
else
mHardware->disableMsgType(CAMERA MSG PREVIEW FRAME); //禁止preview

迏里基本的灶理是, 肖 Camera 硬件抽象屄使用 Overlay 作为取景器諭出时, 只有在


FRAME_CALLBACK_FLAG_ENABLE_MASK 位都有时才使用 Preview 敖据向上扆亻耜送的
功能。 在通常情況下, Camera 的上扆不需要再荻得 Preview 敖据。
startPreviewMo頔)函數用于玕始 Preview, 內容如下所示:
status t CameraService::Client::startPreviewMode() {
II省略錯课灶理部分內容
status_t ret = NO_ERROR;
if (mUseOverlay) { II根据情況做出巨分
if (mSurface != 0) { ret = setOverlay(); l II使用Overlay时的姓理
if (ret != NO_ERROR) return ret;
ret = mHardware->startPreview();
) else { //不使用Overlay时的姓理
mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME); //使能 Preveiw回调
ret = mHardware->startPreview();
if (ret != NO_ERROR) return ret;
if (mSurface != 0) {
mSurface->unregisterBuffers(); //卸栽所有緩沖巨
ret = registerPreviewBuffers(); II注冊预篦緩沖巨

return ret;

startPreviewMode()在 startPreview() 中被调用, 用于姓理只有 Preview (视颜预寛)、 没


有 Recording (视颜呆制)的情況。

handlePreviewDat紙)函數用于预贤數据的姓理, 其中的 介片段如下所示。
if (!mUseOverlay) II不使用Overlay时的姓理

Mutex::Autolock surfaceLock(mSurfaceLock);
if (mSurface ! = NULL) {
mSurface->postBuffer(offset); II友送緩沖巨到视颜榆出没各上

由此可見, 在 Camera 硬件抽象尼没有使用 Overlay, 才调用 !Surace 的 postBuffi函)接


口友送视颜幀米晁示。 实际上, 如果 Camera 硬件抽象扆使用了 Overlay, 迏介预莫工作就
是 Camera 硬件抽象扆姓理的, 不需要 CamaerService 再做灶理。
惡而言之,対于 Preview 的敖据,必然要从 Camera 驱劫程序得到, 又从晟示没各諭出。

迏是 米 一 去的辻程, 如果 Camera 硬件抽象尼没有使用 Overlay, 就要亻耜送 Preview 敖据
到 CamaerService 中; 如果使用了 Overlay, 就要把视颜敖据友送到 Overlay 上。

, 164
第9章 照相机系统 ooe

CamaerService 代碼路往中的 tests/CameraServiceTestJ目呆是針対 Camera 服各的调试文


件。 其中只包含 一 介 CameraServiceTest.cpp 源文件, 将被編诵成 CameraServiceTest 可执行
程序。可以分成几令步骕调用 CameraService 中的內容,使用迏秤方式可以回避 Camera Java
扆的同題, 直接作为实現目杯调试 CameraService 。

�9.2.3 Camera的JNI和Java展

1. Camera 的 JNI

照相机的 JNI 是 対本地尼的 Camera 奕的一介封裝,相比其他的 JNI 实現,照相机部分


需要迸行敖据流的灶理。 其中定又了 一介名稔为 JNICameraContext 的奕, 实現了照相机相
美的几介回调, 主要工作是可以将预莫敖据和拍掇照片的諭出伟入 Java 扆, 対于视颜呆制
的數据則不需要亻考入 Java 展。
Camera 的 JNI 中一 令核心的函數是没置预莫没各的函數, 如下所示:
static void android_hardware_Camera_setPreviewDisplay(JNIEnv *env, jobject thiz,
jobject jSurface) {
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
sp<Surface> surface = NULL;
if (jSurface != NULL) {
surface =
reinterpret_cast<Surface*>(env->GetintField(jSurface, fields.surface));

if camera->setPrev1.ewD1.splay(surface) != NO ERROR) { //錯课, 抛出界常


jniThrowException(env, "java/io/IOException", "setPreviewDisplay failed");

此姓的 Surface 癸即釆自 Java 扆的 android.view 中的 Surface, 特換之后,対调用 Camera


本地炎的没置接口迸行没置。

2. Camera的Java 部分

android.hardware 包中的 Camera 癸是照相机 Java 部分的主体, 其主要方法如下所示:


public vo這setParameters(Camera.Parameters params) //没瑴參敖
public final void setPreviewCallback(Camera.PreviewCallback cb) //没質取泉器回调
public final void setPreviewDisplay(SurfaceHolder holder) ·//没置没各
public final void startPreview() //玕始取景器
public final void stopPreview() //停止取県器
public final void takePicture(Camera. ShutterCallback shutter, //拍援照片
Camera.PictureCallbac)L raw, Camera.PictureCallback jpeg)

Camera.Parameters 奕中包含了各秤没置信息: setPreviewCallback()方法用于没置取景


器回调方法, 仅在程序中需要灶理取景器數据时使用; setPreviewDisplay()表示没置预寛的
晁示没各; startPreview()和 stopPreview()用于控制取景器; takePicture()用于拍援照片。
Camera.S hutterCallback 接口用于灶理即 1 时伺, 具有如下方法:
public abstract void onShutter()

165�
•O o Android 板级支持与硬件相美子系统

Camera.PictureCallback 接口用于姓理拍照回调的致据, 具有如下方法:


public abstract vo這onPictureTaken (byte [ J data f Camera camera)

PictureCallback 中的 onPictureTaken()方法用于得到照片的啟据, 从參敖中亻考入數据。

9.3 照相机 BSP 的結枸

�9.3.1 移植的內容
在 Android 系统中, 照相机系统的杯准化部分是硬件抽象扆的接口, 因此針対特定平
台 Camera 系统的移植包括 Camera 驱劫程序和 Camera 硬件抽象扆。
在 Linux 系统中, Camera 的驱劫程序都是用 Linux 杯准的 Video for Linux 2 (V4L2)
驱劫程序。 从內核空伺到用户空阅, 主要的致据流和控制奕均由 V4L2 驱劫程序的框架來

定乂。 在 Android 系统的实現中, 般也都使用杯准的 V4L2 驱劫程序作为照相机部分的
驱劫程序。
Camera 的硬件抽象尼是位于 V4L2 驱劫程序和 CameraService 之向的部分。 迏是 一介
C十十的接口奕, 需要具体的实現者继承迏介癸, 并实現其中的各介純虛函敖。 Camera 硬件
抽象尼主要实瑰取景器、 视颜呆制、拍掇相片三介方面的功能。 V4L2 驱劫程序 一 般只提供
Video 敖据的荻得,而如何实現视颜预寛,如何向上尼友送敖据,如何把純视颜流和取景器、
视颜汞制等实际並各組組起來,迏些都是 Camera 的硬件抽象尼需要負蠻的方面。自幼対焦、
成像等增強炎的功能, 坯需要使用其他的算法庠和硬件來实現。

�9.3.2 Video for 4 Linux驱劫程序


在 Linux 系统中, Camera 方面的柝准化程序比絞高, 迏今杯准就是 V4L2 驱劫程序,
也是並界比絞通用的方式。
V4L (Video for Linux) 是 Linux 內核中杯准的美于视颜驱幼程序。 V4L 現在的版本是
Video for Linux Two, 简秣 V4L2。 在 Linux 系统中, V4L2 驱劫的 Video 没各节熹路往通常
是 /dev/video/中的 videoX。
V4L2 驱劫対用户空f司提供字符没各,主没各弓为 81。対于视颜没各,次没各兮为 o�63 。
除此之外,次没各弓为 64�127 的是 Radio 没各,次没各另为 192�223 的是 Teletext 没各,
次没各弓为 224�255 的是 VBI (Vertical Blank Interrupt) 没各。
V4L2 驱劫的 Video 没各可以支持捕荻和视颜諭出方式, 通常作为掇像失的驱劫程序,
也可以作为视颜的榆出没各。
Video for Linux 驱幼的架杓如囝 9-2 所示。
V4L2 驱劫的 Video 没各在用户空冏通迥各秤 ioctl 调用迸行控制,并且可以使用 mmap
迸行內存映射。

�166
第9章 照相机系统 oo•

文件接口调用(ioctVmmap)

用户空冏

內核空冏

具体V4L2驱劫
(实現struct video_device)
调用+ I 注J肚
j video_register_device

V4L2驱劫核心
硬件
(v412-dev.c)
操作

调用 屆1ft
字符没各驱幼程序核心

硬件展

掇像� I视蜘榆出没各丨

囷9-2 Video for Linux驱劫的架杓

V4L2 驱劫主要使用的 ioctl 命令值如下所示:


#define VIDIOC_QUERYCAP _IOR('V', 0, struct v412_capabi訌ty) //査洵能力
#define VIDIOC_G_FMT _IOWR('V', 4, struct v412_format) // 荻得格式
#define VIDIOC_S_FMT _IOWR('V', 5, struct v412_format) //没置格式
#define VIDIOC_REQBUFS _IOWR('V', 8, struct v412_requestbuffers) //申清內存
#define VIDIOC_QUERYBUF _IOWR('V', 9, struct v412_buffer) //査洵內存
非define VIDIOC_G_FBUF _IOR('V', 10, struct v412_framebuffer) //荻得Framebuffer
#define VIDIOC_S_FBUF _IOW('V', 11, struct v412_framebuffer) //没置Framebuffer
#define VIDIOC_OVERLAY _IOW('V', 14, int) //没置Overlay
#define VIDIOC_QBUF _IOWR('V', 15, struct v412_buffer) //将內存加入臥列
#define VIDIOC_DQBUF _IOWR('V', 17, struct v412_buffer) //从臥列取出內存
#define VIDIOC_STREAMON _IOW('V', 18, int) //升始流
#define VIDIOC_STREAMOFF _row('V', 19, int) //停止流
#define VIDIOC G CTRL IOWR('V', 27, struct v412 control) II得到控制
#define VIDIOC_S_CTRL _IOWR('V', 28, struct v412_control) II_没置控制

VIDIOC_QUERYCAP 表 示 具 有查洵能 力, 从內 核的驱 劫中荻 取相美的信息;


V4L2_CAP_VIDEO_CAPTURE 表示具有视颜捕荻(掇像�)能力; V4L2_CAP_VIDEO_
OUTPUT 表示具有视颜榆出能力; V4L2_CAP_VIDEO_OVERLAY 表示具有视颜疊加能力。
VIDIOC_G_FMT 和 VIDIOC_S_FMT 用于炅活地迸行流數据格式的荻取和没置, 主要
的 V4L2_buf_type 也巨分成捕荻、 榆出和疊加等多秤奕型。
VIDIOC_REQBUFS 用于申清內存, VIDIOC_QUERYBUF 用于査洵內存。 V4L2_
MEMORY_MMAP 表示映射內核空岡內存, V4L2_MEMORY_USERPTR 表示使用用户空
向內存, V4L2_MEMORY_OVERLAY 表示疊加內存。

167�
• O o Android板级支持与硬件相美子系统

VIDIOC_QBUF、 VIDIOC_DQBUF、 VIDIOC_ STREAMON和VIDIOC_STREAMOFF


等命令主要用于姓理视颜流臥列。其中, VIDIOC_DQBUF 通常佘阻塞,対于视颜捕荻表示
敷据米 到, 対于视颜諭出表示榆出完成。
VIDIOC_ G_CTRL和VIDIOC_ S_CTRL用于控制,控制的內容由杯讠只(id)和值(value)
米表示。
VIDIOC_ G_FBUF、VIDIOC_S_FBUF 和VIDIOC_OVERLAY可用于Overlay预竟方面
的內容。
V4L2驱劫的主要失文件路往如下所示。
e include/linux/videodev.h: V4L第l版的失文件。
e include/linux/videodev2.h: V4L2 的失文件, 定乂主要的數据接口和常量。
e include/media/v412-dev.h: V4L2没各失文件, 定又具体的V4L2驱劫使用其中的接口
注冊等功能。
V4L2驱幼核心实珗为如下文件: driver/media/video/v412-dev.c 。
v412-dev.h中定叉的video_device是V4L2驱劫程序的核心數据結杓, 內容如下所示:
struct video_device{
const struct file_operations *fops; // V4L的文件操作
struct device dev; 没各
// V4L
struct dev.ice *parent; II父没各指針
char narne[32J;
int vfl_type;
int minor; II次没各另
//省略部分video_device的成贝
};

具体的V4L2驱劫程序实瑰video_device 結杓体。
video_device 的注冊和注銷函敖迸行视颜没各注朋:
int video_register_device(struct video_device *vfd, int type, int nr);
void_yideo unregister device (struct video device *vdev);
��-�. '-·�-
具体的V4L2驱劫程序主要实現video_device 中的file_operations結杓体, 坯需要实現

些附加的內容, 在用户空伺迸行ioctl 等调用时,要调用到具体实現的各介函數指針。
呈然V4L2 驱劫程序可以支持视颜捕荻没各, 也可以支持视颜 榆出没各, 但是在实际

的实現中, 捕荻没各和榆出没各的硬件結杓相差很多, 因此 令系统中的迏两秤驱幼程序
需要分別实現。

�9.3.3 硬件抽象展的內容

1. Camera的公共定乂部分

照相机系统的硬件抽象扆相美的两令染文件如下所示。
• Camera.b: 有些常量也在硬件抽象尼中使用。
e CameraParameters.h: 定又Camera系统的參數, 在本地代碼的各介尼次中使用。
Camera.b中, 增加了表示通知消息的枚举值:

�168
第9章 照相机系统 ooe

enum {
CAMERA MSG ERROR = OxOOl, // 錯课消息
CAMERA MSG SHUTTER = Ox002, // 快fl消息
C認ERA MSG FOCUS = Ox004, // 聚焦消息
CAMERA MSG ZOOM = Ox008, // 縮放消息
CAMERA MSG PREVIEW FRAME = OxOlO, II 预篦帔消息
CAMERA MSG VIDEO FRAME = Ox020, II 视颜幀消息
CAMERA MSG POSTVIEW FRAME = Ox040, II 拍照后的停止幀消息
CAMERA MSG RAW IMAGE = Ox080, // 原始效据格式照片消息
CAMERA_MSG_COMPRESSED_IMAGE = OxlOO, // 圧縮格式照片消息
CAMERA MSG ALL MSGS = OxlFF // 所有消息
);

各秤消息奕型表示的也是不同內容, 其中包含敷据流的功能是 CAMERA_MSG_


PREVIEW_FRAMEC预獎幀)、CAMERA_MSG_VIDEO _FRAMEC 视颜呆制幀)、CAMERA_
MSG_POSTVIEW_FRAME (快汀拍照之后的后预竟幀)、 CAMERA_MSG_RAW_IMAGE
(原始格式敖据)和CAMERA_MSG_ COMPRESSED_IMAGEC圧縮格式 數据,通常为JPEG
`` "
格式) 。 CAMERA_MSG_系列的枚举值使用了位的方式來 定叉, 它亻I']可以 或 起米使用。
CameraHardwarelnterface中定又 了几秤回调函敖, 如下所示:
typedef void (*notify_callback} (int32_t msgType, II通知回调
int32_t extl, int32_t ext2, void* user};
typedef void (*data_callback) (int32_t msgType, II敖据回调
const sp<IMemory>& dataPtr, void* user);
typedef void (*data callback timestamp) (nsecs t timestamp, II帶有时冏戳的數据回调
int32_t m竺gTyp_e, const sp<IM_e,rno
_ ry_>.§.

迏里的 回调函敖 的 炎型只有三秤, 迏是三神抽象的哭型, 没有指定具体的用途。


notify_callback用于通知突 的功能, 包含消息奕型(msgType)和两介整敷炎型的护展參敖
(ext 1, ext2); data_callback和data_callback_timestamp用于敷据流伟逸方面的功能, 均包
含消息奕型CmsgType)和數据(!Memory), data_callback_timestamp多包含了一介时阅戳
參 數(timestamp)。

2. Camera硬件抽象扆接口
Android 系统的不同,照相机的硬件抽象尼有所変化。元诒照相机的硬件抽象尼的格式
如何, 都包括以下几介方面的要素。
0接口分为控制突和啟据癸。
. 预莫、 拍照和视颜汞制三令主要功能分別提供控制接口。
0 啟据亻耜逸使用回调机制 來完成。
.通知机制通迥回调机制米完成。
圖不同系统的实現可以自定又接口。
硬件抽象扆主要 的文件失文件为CameraHardwarelnterface.h, 定又 了C++的接口奕需
要根据系统的情況继承实現 。在CameraHardwarelnterface.中, Camera硬件抽象扆的实現通
常需要生成劫态庠libcamera.so。 CameraHardwarelnterface.h硬件抽象屄的対外接口为:
extern "C" int HAL_getNumberOfCameras();
extern "C" void HAL_getCamerainfo(int cameraid, struct Cameralnfo* camerainfo);

169�
•oo Android 板级支持与硬件相美子系统

ext�rn .'.'.C� �p_<CameraHardwareinterfac,e> HAL_openCameraHardware {int cameraid);

CameraHardwarelnterface 炎的定又, 如下所示:


class CarneraHardwareinterface : public virtual RefBase (
public:
virtual -·CarneraHardwareinterface() (J
virtual sp<IMernoryHeap> getPreviewHeap() const = 0;
virtual sp<IMernoryHeap> getRawHeap() const = 0;
virtual void setCallbacks(notify_callback notify_cb,
data callback data cb,
data callback tirnestarnp data cb tirnestarnp,
void* user) = 0;
virtual vo這 enableMsgType(int32_t rnsgType) = 0;
virtual vo這 disableMsgType(int32_t rnsgType) = 0;
virtual bool rnsgTypeEnabled(int32_t rnsgType) = 0;
virtual status_t startPreview() = 0;
virtual bool useOverlay() { return false; J
virtual status_t setOverlay(const sp<Overlay> &overlay) (return BAD_VALUE;}
virtual void stopPreview() = 0;
virtual bool previewEnabled() = 0;
virtual status_t startRecording() = 0;
virtual void stopRecording() = 0;
virtual bool recordingEnabled() = 0;
virtual vo這 releaseRecordingFrarne(const sp<IMernory>& rnern) = 0;
virtual status_t autoFocus() = 0;
virtual status_t cancelAutoFocus() = 0;
virtual status_t takePicture() = 0;
virtual status_t cancelPicture() = 0;
virtual status_t setPararneters(const CarneraPararneters& pararns) = 0;
virtual CameraParameters getPararneters() const = 0;
virtual status_t sendCornrnand(int32_t crnd, int32_t argl, int32_t arg2) = O;
virtual void release() = 0;
virtual status_t durnp(int fd, const Vector<Stringl6>& args) const = 0;

除了几介控制函敖之外, 用于數据流的回调函數由 setCallbacks() 、 enableMsgType(),


disableMsgType()和 msgTypeEnabled()迏几介函敷统 一 灶理, setCallbacks()可以没置三介奕
型的回调函數指針; enableMsgType()和 disableMsgType()用于指定前面的各秤消息哭型;
msgTypeEnabled()用于査洵某介消息的回调函數是否使能。

3 提示:按照迏秤消息旋理机制, 每一秤消息都可以使用三今回调函敖, 但是通常情


況下只使用和自己奐型相美的。

例如, 没置使能了 CAMERA_MSG_PREVIEW_FRAME 表示要求得到 preview 的數


据, 迏祥翌 startPreview() 升始之后, 每 一 幀敖 据到釆的时候, 将调用前 面没 置 的
data_callback 奕型的回调函數, 将 CAMERA_MSG_PREVIEW _FRAME 作为第一 介參敖,
以 !Memory 哭型表示的 preview 敖据作为第二介參數, 回调函敖没置的 user 指針作为第
三介參敖亻夸回。
实际上, 以上 Camera 硬件抽象扆回调函敖机制的変化并没有引起本原巨別。 早期
Camera 硬件抽象扆的接口比絞直接, 最新 Camera 硬件抽象扆的接口比絞统 一 、 規整, 更
有利于功能的护展。

, 170
第9章 照相机系统 ooe

在最新的 Camera系统中, 坯新增了sendCommand()的功能。首先在Camera.b中, 增


加了命令及其返回值:
enum { / / sendCommand函數使用的命令炎型cmd癸型
CAMERA_CMD_START_SMOOTH_ZOOM = 1,
CAMERA_CMD_STOP_SMOOTH_ZOOM = 2,
CAMERA CMD SET DISPLAY ORIENTATION = 3,
);
enum { //借课哭型
CAMERA ERROR UKNOWN = 1, CAMERA ERROR SERVER DIED = 100
};

CameraHardwarelnterface癸定又的相矢函敖如下所示:
virtual status_t sendCommand(int32_t cmd, int32_t argl, int32_t arg2) = 0;

在 sendCommand()函數中, 第 一介參敖表示 命令, 后面坯帶有两介自定叉的參敖 。 平


命令增加时, 只需要增加命令奕型即可。
sendCommand()函致的意又是Camera系统包含更多可护展的功能。 由于Camera系统

是 介 比絞夏朵的系统, 各神不同的硬件平台所包含的功能也是各神 各祥的, 以 后的
Camera硬件可能坯有更丰富的功能。在迏秤情況下, 逐漸增加Camera系统 各令昆次的接
口不利于兼容性, 因此增加sendCommand()函敖作为统 一的接口 。
Camera系统的硬件抽象尼涉及 Overlay系统使用的阅題,如果 Overlay系统没有完成,
坯需要先使用元Overlay的方式迸行调试, 二者的差別也是比絞大的 。

3. Camera硬件抽象展夫鍵实瑰

Camera硬件抽象扆本身是 介自下而 上佳逸视颜敷据的功能模玦,其接口大都是控制
奕的接口,而數据流由硬件抽象扆 调用上展没置的回调函敖亻苟送給上扆。Camera硬件抽象
尼的 控制癸接口均为昇步接口, 调用时是各介函數佘立即返回。
Camera硬件抽象扆主要灶理以下三秤並各:
0 取景器预寛(使用YUV原始敷据格式, 友送到视颜榆出没各)。
口拍掇照片(可以使用原始數据或者圧縮囹像數据)。
0视颜汞制(将敖据伟送給视颜編碼器程序)。
取景器预寛(Preview)的 主要步骕如下所示。
h在初始化的近程中, 建立预薨致据的內存臥列(可以使用多秤方式)。
3 在startPreview()的实瑰中, 建立预寛綫程 。
h 在预槳鈛程的循坏中, 等待视颜敖据的到迏。
b 视颜幀到迏后使用预竟回调的机制(CAMERA_MSG_PREVIEW_FRAME), 将视
颜幀的致据向上展伟送 。
如果使用Overlay实現取景器预竟, 則有以下几令変化 。
(I)实現useOverlay()函敖, 必多頁返回true, 上尼才佘使用Overlay。
(2)在setOverlay()函數中, 从!Surface接口荻得Overlay哭。
(3)在预竟紱程的循坏中, 直接将视颜敖据通迥Overlay 的接口亻考送, 可以不使用预

171�
• O O Android板级支持与硬件相美子系统

贤 回调函致亻耜送预莫敷据 。
Camera 硬件抽象尼 useOverlay()和setOverlay()两介接口默从已經实現: useOverlay()
返回false; setOverlay()为元效, 表示默从Camera硬件抽象扆不是用Overlay 。
拍掇照片(Picture)的主要步骕如下所示。
( l ) takePicture() 函 數表示升始拍掇, 可以建立单袖的綫程米灶理。
(2) 使用回调机制亻考送數据: 如果得到原始格式(通常是 YUV 的某秤格式, 如
yuv422sp)的致据, 使用CAMERA_MSG_RAW_IMAGE将敷据亻耜送; 如果得到圧縮囹像
(通常JPEG格式), 使用CAMERA_MSG_COMPRESSED_IMAGE将敷据伟送。
Camera硬件抽象展可能从驱劫得到原始敷据CRAW), 也可能 得到JPEG敖据(例如,
使用Smart擬像失硬件时), 在Camera硬件抽象扆 可以将原始 數据圧縮成JPEG敖据, 迏
时依然要 使用CAMERA_MSG_ COMPRESSED_IMAGE的方式亻耜送。
呆制视颜(Recording)的主要步驟如下所示。
D 在 startRecording()的实珗中, 升始 汞制的准各, 汞制视颜可以使用自己的紱程,
也可以使用预典綫程 。
3一
苗某 介视颜幀到米的 时候, 通迥泵制 回调机制( 使用CAMERA_MSG_ VIDEO
FRAME)将视颜幀向上友送。
�releaseRecordingFrame()被调用后,表示上尼通知Camera硬件抽象扆,迏 一 幀的內
存 已經用完, 可以迸行下一次的灶理。
在视颜汞制的迥 程中,视颜幀需要亻耜送Camera硬件抽象扆給视颜編碼器迸行 編碼。驱
劫程序亻考送上米的视颜泵制的敷据和取景器预贤的 數据通常为同 一 數据(在一 玦內存中) 。
releaseRecordingFrame()被调用时, 通常表示編碼器已經完成了対肖前视颜幀的 編碣, 対迏
坎內存 迸行秤放。 在releaseRecordingFrame()函數的实現中, 可以没置柝志位,杯记幀內存
可以再次 使用。

提示: Camera硬件抽象扆向上屋佳送的是讽颎帙敖据,在編碼器使用完迏一帙敖据
之前,Camera硬件抽象屋不能写送決內存, 否則令引是編瑪的混舌L。

結合驱劫程序的情況, 対于Linux系统而言, 掇像失驱劫部分大多使用V4L2 驱劫程


序, 在此灶主要的灶理流程如下所示 。
.如果使用映射內核內存的方式 (V4L2_MEMORY_MMAP), 杓 建预莫的內存(以
MemoryHeapBase結杓为封裝), 需要从 V4L2驱幼程序中 得到內存指針。
.如果使用用户空阅內存的方式(V4L2_MEMORY_USERPTR), MemoryHeapBase中
升辟的內存是在用户空向建立的 。
.在预寛的綫程中, 使用VIDIOC_DQBUF 调用阻塞等待视颜幀的到米, 灶理完成 后
将 幀內存 再次圧入臥列, 等待下一幀的到米 。
使用VIDIOC_QBUF调用
事实上,在Camera硬件抽象扆调用
驱劫程序方面,通常灶理的仅仅是得到视颜幀的迥
程, 至于视颜幀 用做Preview述是Recording , 与驱劫程序方面元美。

�172
第9章 照相机系统 ooe

�9.3.4 照相机系统上下展的夫系

1. Camera硬件抽象展实珉対上展的影珦
Android中的Camera子系统対上尼提供的接口也分为两令扆次。
.本地API: 通迥 libui或者 libcamera_ client向Android本地尼提供的接口。
• JavaAPI: 作为 android.hardware.camera奕向Java提供的接口。
Camera系统的JavaAPI 主要用于照相机皮用程序,迏介皮用程序是Android中预置的,
也可以根据Camera系统的API重新实珗。 此外, 基于Camera的Java框架尼的APL坯可
以扭描讠只別突的程序, 例如人脫讠只別、 名片讠只別、 糸形碼讠只別等, 迏奕程序通常需要利用
照相机的取景器敖据。
Camera系统的本地API, 主要提供給视颜汞制或者视颜屯话等需要荻取视颜幀的功能,
此时整介Camera系统是作为视颜荻取坏节來使用的。
Camera系统敖据流的走向有各秤炅活的組合, 有几神典型的汤景。
第一秤汤景: 在照相机皮用中, 视颜预萸的 Preview敖据, 通常已經在CameraService
扆或者Camera的硬件抽象扆伟送給晁示没各, 因此上尼(尤其是Java扆)不需要得到视
颜预竟敖据。
第二秤汤景: 在扭描讠只另IJ炎的皮用中, 视颜预寛(取景器)敷据需要通近各尼的回调
机制亻耜送給Java扆。
第三秤汤景:在视颜荻取突的程序中,视颜呆制敖据从Camera的硬件抽象扆伟送給編
碼器迸行編碼, 通常也不令伟逸給上屄。

2. 照相机系统的數据流情況
由于照相机系统的數据流比絞大, 而且分成几秤不同的炎型, 因此,熟悉Camera中主
要數据流的灶理流程, 対于Camera硬件抽象扆的实現和调试Camera系统都将有很大的牾
助。 迏里主要从预萸數据、 拍照數据、 视颜汞制敷据三令方面分別介紹。
Camera系统的预寛敷据主要有三神走向:
Cl)在Camera硬件抽象扆中, 直接亻耜送給Overlay。
(2)在CameraService中, 调用 !Surface的postBuffer接口, 伟送出敖据。
(3) Camera奕通迥Callback伟送給上扆, 由上扆灶理。
在Android照相机/掇像机皮用程序中,使用的是(2)和(3) 迏两神方法, 具体是(2)
坯是 (3), 由CameraService珙取Camera硬件抽象屄的 use
Overlay 接口米实現 。
Camera的预萸敖据流的走向如囹9-3所示。
从囷9-3 中可見,Preview的數据首先必須从Camera的驱幼程序中到迏Camera硬件抽
象屄。 如果使用 Overlay , 敖据直接从 Camera 硬件抽象扆友送到 Overlay, 如果不使用
Overlay, Camera硬件抽象扆把敖据友送給CameraService, CameraService通迥!Surface榆
出到!Surface。

173�
•oO Android板级支持与硬件相美子系统

android. hardware.
PostEventFromNat;;-i Camera

Camera的JNI
.••. •.•••一T
post_event
3仗用回调
送入上尼
Set

CameraService
2. 送入 预贤敷据
!Surface晁示

.一一一一一一一一一一一一
掇像染致据

!Surface

Android系统昆

Linux 驱劫居
Camera没各

困9-3 Camera的预竟敷据流的走向

提示: 如果使用!Su rface的postBuffer,需要綬Androi d系统的Surface系統友送盅


示敖据, 也要綬辻颜色格式特換和臺加。

事实上,元讠它使用Overlay与否,(I)或者(2)方式已經能实現取景器预座的效果了。
如果Java扆次坯需要得到P review敖据, 則需要通迥回调函數逐毘向上岌送。 本地尼通迥
回调函敷友送,JNI向Java尼通迥post_event反向调用Java的接口, Camera的Java代碼
的PostEventFromNativeO函敖可以荻得预典的數据。
Camera拍掇照片的迥程比絞简单,対于亻考送原始數据的Camera驱幼程序而言, 荻得
拍掇照片的數据和预莫敷据仅有大小的巨別: 预薨敷据一 般仅有屏幕大小, 拍掇照片的數
据可以得到尽量大的數据。 因此在迏秤情況下, 蚩tae
k: Pic tureO函數被调用时, 需要切換从
驱幼得到的视颜敖据的大小。在Camera的硬件抽象扆中,可以将原始敖据迸行編碼,得到
JPEG敷据, 然后通迥回调函敖将敖据上亻寺。 即使得到JPEG敷据, 也需要将其放置在內存
中, 而不是直接写文件。 写文件的工作是由Java皮用房完成的。
Camera系统迸行视颜呆制的迥程,也涉及上下尼的各令方面,主要目的就是从Camera
驱劫中得到敖据, 直到亻耜送給视頻的編碼器。
如果仅仅从视颜呆制的角度, 迏是 一令比絞简单的迥程: 不需要亻耜送給Java扆, 只亻夸
送給本地的編碼器。 其夏朵的方面在于, 対于掇像机的陘用,往往是在视颜呆制的迥程中,
取景器的预莫坯在继维迸行中, 二者使用的可能是同源數据。
Camera的汞制敖据流的走向如囹9-4所示。

, 174
第9章 照相机系统 ooe

视颜汞制器
____
.,._汞制數据
预竟敖据
Set

Surface
' CameraService ·一一一一一一一一一一一一
掇像失致据

Start I Stop I
ReleaseFrame

!Surface

Android系统展

Linux驱劫扆

Camera没各

囷9-4 Camera的汞制敖据流的走向

由囹9-4可見,通常情況下视颜汞制的數据也是从Camera驱劫中得到的,需要将其亻夸
送給视颜呆制器中的編碼器 。 迏介敷据玦通常和取景器共用。 一秤經典的方式就是數据內
存本身是从Overlay中映射出米的, 从Camera驱劫 将敷据放到 迏炔內存中即实現了预贤的
效果。 在需要视颜呆制的时候,就是通迥泵制机制的回调函敖亻耜送給上尼。 为了避免敷据
的互相干涉,必多薁要上尼调用 releaseRecordingFrameO函敖秤放视颜幀(表示編碼結束),
Camera的硬件抽象扆才可以再次使用迏玦內存 。

9.4 照相机 BSP 的实瑰

�9.4.1 粧实瑰
- '`
Android中已經实現了 今Camera硬件抽象扆的 粧(Stub)", 可以根据宏米迸行配
置。 迏令粧使用假的方式, 实現取景器预薨和拍掇照片等功能。 USE_CAMERA_STUB編
诵宏为true 时, 将生成靜态庠libcamerastub.a, 被照相机的服各部分直接镱接使用。
Camera硬件抽象扆粧实珗使用黑白相阅的格子代替实际米自硬件的视颜流,送祥可以
在不接蝕硬件的情況下,辻Android的Camera系统运行。 晁然由于没有特殊的视颜榆出没
各,Camera硬件抽象扆粧实瑰是不使用Overlay的。
迏祥整介Camera模炔可以在没有硬件的情況下編诵通迥并可以假裝运行。它亻i'J在文件
CarneraHardwareStub.cpp和FakeCamera.cpp中 实現。
Camera硬件抽象扆粧实珗包含的几令文件如下所示。
a CameraHardwareStub. *: CarneraHardwarelnterface接口的实現。
a FakeCamera.h和FakeCarnera.cpp: 实現假的Camera的黑白格取景器效果。

175�
•oO Android板级支持与硬件相美子系统

• CannedJpeg.h: 包含 - 坎 JPEG 敖据, 用于拍擬照片时作为 JPEG 敖据。



Camera的粧实現中只支持 令后置掇像染, 其定又如下所示:
static Camerainfo sCamerainfo[J = {
{ CAMERA FACING BACK, 90, II orientation }
);


FakeCamera.h和FakeCamera.cpp实現了奕FakeCamera, 迏今炎的功能是完成 令假
的掇像染諭入敖据的內存, 定又如下所示:
class FakeCamera {
public:
FakeCamera(int width, int height);
-FakeCamera();
void setSize(int width, int height);
VO這getNextFrameAsRgb565(uintl6_t*buffer); //荻取RGB565格式的 `' 预篦幀"
VO這getNextFrameAsYuv422(uintB_t*buffer); //荻取YUV422格式的"预篦帜"
status t dump(int fd, const Vector<String16>& args);
II省略部分丙容

在FakeCarnera接口中,可以定乂寛和高,它亻i'J 模姒了实际系统中 援像失諭入敖据的大


小, 支持 RGB565和YUV422 两秤颜色空阅格式。 送介敖据可以根据时冏変化, 因此得到
'`
黑白相向的格子的 预贤"是劫态的。
在CameraHardwareStub中迸行參敖没置后, 佘调用initHeapLo cked()函敖, 如下所示:
void CameraHardwareStub::initHeapLocked()(
int picture_width, picture_height;
mParameters.getPictureSize(&picture_width, &picture_height);
mRawHeap = new MemoryHeapBase(picture width* 2* picture height);//建立內存堆
int preview_width, preview_height;
mParameters.getPreviewSize(&preview width,&preview height); II从參敖中荻取信息
int how big= preview width* preview_height* 2;
II省略部分錯课赴理內容
mPreviewFrameSize= how big;
mPreviewHeap= new MemoryHeapBase(mPreviewFrameSize* kBufferCount);
for (int i = 0; i < kBufferCount; i++) ( //建立內存臥列(kBufferCount为4)
mBuffers[i) = new MemoryBase(mPreviewHeap,
i* mPreviewFrameSize, mPreviewFrameSize);

delete mFakeCamera;
mFakeCamera = new FakeCamera{preview_width, preview_height);

在迏介迥程中, 建立了两玦內存(MemoryHeapBase): mRawHeap 表示 一 令拍照照片



的內存, rnPreviewHeap表示取景器预薨的內存 。 由于取景器预竟的內容是 令臥列, 因此

在rnPreviewHeap中建立kBufferCount (为4)介MemoryBase。 建立 介FakeCamera作为
假的援像失榆入敖据的來源。

CameraHardwareStub的 startPreview()中建立 介紱程, 如下所示:
status_t CameraHardwareStub:: startPreview() {
Mutex: :Autolock lock (mLock);
// .••.•.省略部分借课灶理內容
mPreviewThread= new PreviewThread(this); //建立视颜预贤的綫程

�176
第9章 照相机系统 ooe
return NO_ERROR;

在 PreviewThread 鈛程中通迥调用预獎的回调机制, 实珗预覧敖据的數据亻耜逆給上尼


(也就是 CameraService)。
在预莫的紱程 previewThread 中, 建立 一 令循坏, 得到假的掇像失榆入數据的米源,
并通迥预莫的回调函數将榆出伟到上扆。 內容如下所示:
int CameraHardwareStub: :previewThread {){
mLock.lock();
int previewFrameRate = mParameters.getPreviewFrameRate();
ssize t offset = mCurrentPreviewFrame * mPreviewFrameS1.ze; //荻取內存偏移
sp<MemoryHeapBase> heap = mPreviewHeap;
FakeCamera* fakeCamera = mFakeCamera; //荻得FakeCamera�
sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame];
mLock.unlock();
if (buffer != 0) {
int delay = (int)(1000000. Of/float(previewFrameRate)); //模姒每幀的延迟时冏
void *base = heap->base(); //荻得內存地址
uint8 t *frame = ((uint8 t *)base) + offset; II荻得视颜幀
fakeCamera->getNextFrameAsYuv422(frame);
if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) II调用Callback向上展友送敷据
mDataCb(CAMERA MSG PREVIEW FRAME, buffer, mCallbackCookie);
mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
usleep(delay);

return NO_ERROR;

'`
Camera 硬件抽象扆粧实瑰的取景器敖据米自 假的援像 �(FakeCamera)", 可以得到
敷据, 并使用回调函數以CAMERA_MSG_PREVIEW_FRAME宏为參敖将敷据送向上尼。
迏里使用的 mDataCb 是上尼 CameraService, 通迥 setCallbacksO函敖没置。 如果使用真正
的硬件, 视颜敷据的荻得需要等待, 因此荻取掇像失的致据內存一 般是一 介阻塞操作, 在
FakeCamera 中由于没有实际硬件和驱劫程序, 使用延时釆代替迏秤效果。
takePicture()函數在拍掇照片时被调用, 它也保存了回调函數的指針, 并建立了拍掇照
片的紱程。
int CameraHardwareStul;>::pictureThread(){
if (mMsgEnabled & CAMERA_MSG_SHUTTER)
mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie); //快fl回调机制
江(mMsgEnabled & CAMERA_MSG_RAW_IMAGE) { //伟送原始啟据的姓理
int w, h;
mParameters.getPictureSize(&w, &h);
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
FakeCamera cam(w, h);
cam. getNextFrameAsYuv422((uint8_t *)mRawHeap->base()); //荻得指針
mDataCb(CAMERA MSG RAW IMAGE, mem, mCallbackCookie); II伟送敷据

if (mMsgEnabled & CAMERA MSG COMPRESSED IMAGE) { //伟送JPEG敖据的姓理
sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);

177�
•oO Android 板级支持与硬件相美子系统

return NO_ERROR;

因为最后直接返回, 因此拍掇照片的紱程实际上不是 一 令循坏, 只是 一 令单次运行的


函數。 迏里首先调用 CAMERA_MSG_ SHUTTER 向上通知的快 n 信息。然后分別亻耜送原始
敷据和圧縮 囹 像 敖据: 原 始敖据 使 用 FakeCamera 的敖据, 通迥 CAMERA_MSG_

RAW_IMAGE 宏友送;圧縮囹像敖据使用 CannedJpeg.h 中定叉的 JPEG 敖据文件向上友送。


CameraHardwareStub 可以在没有掇像染硬件的情況下调试照相机的皮用程序。 經近简
单的修改, 实瑰 startRecording()、 stopRecording()和 releaseRecordingFrame() 后, 坯可以作
为假的援像机榆入没各米使用。

�9.4.2 Nexus One系统的Camera实瑰


Nexus One 的照相机使用高通的 MSM 平台 CQSD 灶理器)的通用实珗, 対于高通平
台, 援像失是灶理器內部部件, 但是所连接的掇像失伟感器不同。

1. 驱劫程序
Nexus One 照相机部分驱劫程序的主要文件在 drivers/media/video/msm/ 目汞中。几介实
瑰的文件內容如下所示。
e msm_v4l2.c: V4L2 驱劫程序的入口文件。
e msm_camera.c: 公用的庠函敖。
e s5k3e2fx.c: 掇像染亻耜感器驱劫文件, 使用 I2C 接口控制。
MSM 平台的 Camera 驱劫程序的結杓如囹 9-5 所示。

用户空冏 /dev/msm_camera/•

內核空阅
Sensor驱劫
msm_v4l2.c (s5k3e2fx等)

msm_camera_drv _starl()
msm_ v412_registe而
msm_ v412 _unregister()

msm camera.c

囹9-5 MSM 平台的 Camera 驱劫程序的結杓

msm_camera.h 是 MSM 掇像失相美的失文件。其中定又了各秤額外的 ioctl 命令,其片


段如下所示:
#define MSM CAM IOCTL MAGIC 'rn'
#define MSM_CAM_IOCTL_GET_SENSOR_INFO \
_IOR(MSM_CAM_IOCTL_MAGIC, 1, struct rnsrn_carnsensor_info *)
#define MSM_CAM_IOCTL_REGISTER_PMEM \
_IOW(MSM_CAM_IOCTL_MAGIC, 2, struct rnsrn_prnern_info *)
ltdefine MSM_CAM_IOCTL_UNREGISTER_PMEM _IOW(MSM_CAM_IOCTL_MAGIC, 3, unsigned)
9efiqe MSM_C聖一IOCT互』#蝌�_COMMAND\

�178
第9章照相机系统 ooe

IOW{MSM CAM IOCTL MAGIC, 4, struct msm ctrl cmd *)


I/define MSM CAM IOCTL CONFIG VFE \
IOW{MSM CAM IOCTL MAGIC, 5, struct msm camera vfe cfg cmd *)
II省略部分ioctl命令弓的內容
I/define MSM CAM IOCTL PP IOW{MSM CAM IOCTL MAGIC, 19, uint8 t *)
扣define MSM CAM IOCTL PP DONE\
IOW{MSM CAM IOCTL MAGIC, 20, struct msm snapshot pp status *)
j/define MSM CAM IOCTL SENSOR IO CFG \
IOW{MSM CAM IOCTL MAGIC, 21, struct sensor_cfg_data *)
#define MSM CAMERA LED OFF 0
j/define MSM CAMERA LED LOW 1
#define MSM CAMERA LED HIGH 2
I/define MSM_CAM_IOCTL_FLASH_LED_CFG _IOW(MSM_CAM_IOCTL_MAGIC, 22, unsigned *)
j/define MSM_CAM_IOCTL_UNBLOCK_POLL_FRAME _IO(MSM_CAM_IOCTL_MAGIC, 23)
ildefine MSM CAM IOCTL CTRL COMMAND 2\
_IOW(MSM_CAM_IOCTL_MAGIC, 24, struct msm_ctrl_cmd *)
#defiJle MSM CAM IOCTL ENABLE OUTPUT IND IOW(MSM CAM IOCTL MAGIC, 25, uint32 t *)

msm_camera.c 实瑰了鋪助MSM的 Camera系统运行功能的文件,其中既包含提供給


內 核调用 的 文 件 ,也 包 含 提 供 給用户空岡的接 口 ,在用户空岡的没各 燕
节 也就是
/dev/msm_camera/中的三介没各(配置没各config O、控制没各controlO和幀數据没各
frameO), 以上ioctl命令也是为迏些没各节燕使用的。
msm_camera.c为內核空囘提供了接口,如下所示:
int msm_v412_register(struct msm_v412_driver *drv) {) //注朋msm_v412_driver驱劫
EXPORT_SYMBOL(msm_v412_register);
int msm_v412_unregister(struct msm_v412_driver *drv) {)//注悄msm_v412_driver驱劫
EXPORT_SYMBOL(msm_v412_unregister);
int msm_camera_drv_start(struct platform_device *dev, //注朋升始Camera驱劫驱劫
int (*sensor_probe) (const struct msm_camera_sensor_info *,
struct msm sensor ctrl * )){)
EXPORT_SYMBOL (msm_camera_drv_start);


msm_v412_reg isterO和 msm_v412_unreg ister()函 數供调用 msm_v412.c用于刨建 介
msm_v412_driver奕型的驱劫; msm_camera_drv_start()供Camera的 Sensor驱劫程序的调用,
參敖为Sensor的 probe函數 ,迏介函數用于注朋msm_camera_ sensor_info和msm_sensor_ctrl
奕型的結杓体,表示实际Camera Sensor的实瑰。

2. 硬件抽象屏

MSM平台Camera 的硬件抽象扆已經包含在Android代碼中,迏部分內容路往为
hardware/msm7k/libcamera, 其中包含以下几介文件。
e camera_ifc.h: Camera接口 中常量的定叉。
e QualcommCameraHardware.h: 硬件抽象扆的失文件。
e Qualcon1mCameraHardware.cpp: 硬件抽象尼的实瑰。
Camera 硬件抽象扆的初始化阶段,劫态打玕OEM的实現庠,liboemcamera.so, 取出
其中的符弓,事实上,迏里主要的功能是 在 liboemcamera.so取出的符另中实現的。本硬
件抽象尼 提供的是框架,使用"yuv420sp"格式作为预莫的格式,使用"jpeg"作为榆出囹像
的格式。
QualcommCameraHardware.h 中定又奕MemPool, 迏介突表示 一介 內存。PmemPool

179�
• O O Android板级支持与硬件相美子系统

和AshmemPool是MemPool的继承者,PreviewPmemPool和RawPmemPool是PmemPool
的继承者 。
PmemPool是通迥 MemoryHeapPmem建立 在pmem 上的內存。在迏介驱劫程序的实現
中,原始數据的內存堆通這/dev/pmem_camera没各节煮得到,为RawPmemPool奕;预獎
的啟据 堆通迥/dev/pmem_adsp没各节燕得到,为PreviewPmemPool炎; JPEG 的內存堆是
通述AshmemPool建立在軟件分配的內存上的。

�9.4.3 Nexus S系统的Camera实班


Nexus S平台的 Camera 包括 主掇像失和前置掇像�. 在硬件上,三星灶理器的照相机
控制器FIMC(Fully Interactive Mobile Camera)可以支持两令掇像染的伟感器,并有三介
硬件 通道対其迸行灶理。Nexus S平台 驱劫程序和硬件抽象扆中都有対皮的支持部分。

1. 驱劫程序
Camera 驱劫程序和视颜榆出部分,同厲FIMC部分。 视颜部分在drivers/media/video/
目泵中,包括亻考感器部分和v412部分。
e s5k4ecgx.c 和 s5ka3dfx.c : I2C连接的掇像 失亻夸感器 驱劫程序 , 驱 劫的名稔为
S5K4ECGX和S5KA3DFX, 体現在sys文件系统的/sys/bus/i2c/drivers/目呆中 。
e samsung/fimc/: 三星灶理器 v412 视颜榆出的驱劫程序 。其中fimc_capture.c是视颜
捕荻没各的实瑰,将生成/dev/videoO和/dev/video2 分別是主掇像染和前置掇像失的
没各节缸在sys文件系统的/sys/class/video4linux/video0和video2也是相失的內容。
掇像染亻考感器作为狹立的驱劫程序注冊到VideoFor Linux的驱幼框架中, 迏也是比絞
常用的結杓。
fimc_capture.c中則通迥f皿c_hw:XXX()等函數対寄存器迸行硬件操作。
include/linux/videodev2_samsung.h中定叉了护展的操作( CID ), 如下所示:
嵒 define V4L2 CID ROTATION (V4L2 CID ' PRIVATE ' BASE + 0)
扣 define V4L2 CID PADDR Y (V4L2_CID_PRIVATE_BASE + 1)
itdefine V4L2 CID PADDR CB (V4L2 CID PRIVATE BASE t 2)
itdefine V4L2 CID PADDR CR (V4L2 CID PRIVATE BASE + 3)
#define V4L2 CID PADDR CBCR (V4L2_CID_PRIVATE_BASE + 4)
I I ...... 省略其他操作
#define V_4L2_�ID_§_TRE碴辶fAU哇辶 (\.'.JJ.,2 <:;,J:J;> PRIVA_IE_ElASE :t_ 53)

此灶定又的 ioctl的命令都是三星灶理器的特殊控制命令,供用户空岡调用 。

提示:三星灶理器在硬件上支持将照相机的榆入直接友送到昰示控制器(D isplay
Controller)上,但是在Android预兕敖据述是必娥佳入上扆。

2. 硬件抽象展

Nexus S 系统 照相机硬件抽象扆的代碼路往为: device/samsung/crespo/libcamera/, 其中


包括以下文件。
e SecCameraHWlnterface.*: 硬件抽象扆接口CameraHardwarelnterface的实珗。

, 180
第 9章 照相机系统 ooe

• SecCamera. *: 下尼的实瑰奕, 调用了驱劫程序。


SecCamera.cpp 中的若干介 frmc_v412_s_*是対 V4L2 驱幼程序的封裝,其中的旋特等功
能是基于特定的 ioctl 米命令 (V4L2_CID_ROTATION 等)实現的, 數值的定叉米自內核中
的 videodev2_samsung.h 文件。
CameraHardwareSec 继承 CameraHardwarelnterface, 其初始化迥程如下所示:
int SecCamera::initCarnera(int index) {
int ret= O;
if (!rn flag init) {
m camera af flag= -1;
m cam fd temp= -1;
m cam fd2 temp= -1;
m cam fd= open(CAMERA DEV NAME, 0 RDWR); //主掇像:¼(后箕)
II省略錯课姓理
ret= fimc_v412_querycap(m_cam_fd);
if (!fimc v412 enuminput(m cam fd, index)) return刁.;
ret = fimc v412 s input(m cam fd, index);
m cam fd2= open(CAMERA DEV NAME2, 0 RDWR); //前置攝像失
II省略錯课灶理
ret= fimc_v412_querycap(m_cam_fd2);
CHECK(ret);
if (!fimc_v412_enuminput(m_cam_fd2, index)) return 一 l;
ret= fimc v412 s input(m cam fd2, index);
CHECK(ret);
m camera這= index;
switch (m camera id) {
case CAMERA ID FRONT: //index== 1的情況,主擬像�(后置)
m_preview_max_width = MAX FRONT CAMERA PREVIEW WIDTH;
m_preview_max_height = MAX_FRONT_CAMERA一PREVIEW_HEIGHT;
m_snapshot_max_width = MAX_FRONT_CAMERA_SNAPSHOT」HDTH;
m_snapshot_max_height = MAX_FRONT_CAMERA_SNAPSHOT_HEIGHT;
break;
case CAMERA ID BACK: //index== 0的情況,前置掇像失
m_preview_max_width = MAX_BACK_CAMERA一PREVIEW_WIDTH;
m_preview_max_height = MAX_BACK_CAMERA_PREVIEW_HEIGHT;
m_snapshot_max_width = MAX_BACK_CAMERA_SNAPSHOT_WIDTH;
m snapshot_max_height = MAX_BACK_CAMERA_SNAPSHOT_HEIGHT;
break;

setExifFixedAttribute();
m flag init= l;

return 0;

初始化的近程最終通迥调用 V4L2 驱幼程序來完成, 根据主掇像�(后置)和前置掇


像失的差別, 没置了不同參敖。
Nexus S 系统照相机的实瑰使用了 Overlay, previewThread()是取景器预莫鈛程的实瑰
函數, 其主要实現如下所示:
if (mUseOverlay) {
int ret;
overlay_buffer_t overlay_buffer;
mOverlayBufferidx =
" 1;
memcpy(static_cast<unsigned c袒i'r*>(mPreviewHeap->base()J + offset

181�
•oo Android 板级支持与硬件相美子系统

+ frame_size + sizeof(phyYAddr) + sizeof(phyCAddr),


&mOverlayBufferidx, sizeof(mOverlayBufferidx));
ret = mOverlay->queueBuffer( //友送內存
(void*)(static_cast<unsigned char *>(mPreviewHeap->base())
+ (offset + frame_size)));
if (ret == -1 ) (//省略錯课信息打印 }
else if (ret ! = ALL_BUFFERS_FLUSHED) { //取出內存
ret = mOverlay->dequeueBuffer(&overlay_buffer);
if (ret == 一1) (//省略措淚信息打印 )

江(mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) {


mDataCb(CAMERA MSG PREVIEW FRAME, buffer, mCallbackCookie); //向上扆友送

在取景器预竟的时候, 敖据米自掇像�' 而从 Overlay 迸行视颜的諭出。 迏里的內存


实际上是 一 玦连维的內存, 包括大小相等的几令部分, 每令部分対皮一 次的內存幀, 而它
仞连维起米組成一 令臥列。 queueBuffer() 和 dequeueBuffer()的调用完成了向 Overlay 友送视
颜幀。 使用 Overlay 就可以实瑰取景器的预覧, 通常情況下, 上尼 (Camera 服各)不需
要得到取景器數据, 只有圭需要支持扭描枳別奕皮用的时候, 通迥回调将取景器敖据亻考
入上扆。
者迸行视颜汞制时, releaseRecordingFrame()用于实珗梓放某 一 幀视颜的工作, 其內容
如下所示:
void CameraHardwareSec::releaseRecordingFrame(const sp<IMemory>& mem) {
ssize t offset;
sp<IMemoryHeap> heap = mem->getMemory(&offset, NULL);
struct addrs *addrs = (struct addrs *)((uint8 _t *)heap->base() + offset);
mSecCamera->releaseRecordFrame(addrs->buf index); II由下展实瑰记呆索引

此姓将由 SecCamera 的实現记泵索引, 在视颜汞制幀没有被秤放的情況下, 不佘対內


存填入新的數据。
视颜呆制不合使用单袖的紱程來完成,只需要在预寛的紱程內通迥回调友送敖据即可,
因此 previewThread() 中相美的內容如下所示:
if (mRecordRunning == true) (
index = mSecCamera->getRecordFrame(); //得到幀的索引
II省略措课灶理
phyYAddr = mSecCamera->getRecPhyAddrY(index);
phyCAddr = mSecCamera->getRecPhyAddrC(index);
//省略錯课姓理
addrs = (struct addrs *)mRecordHeap->base();
sp<MemoryBase> buffer = new MemoryBase(mRecordHeap,
index * sizeof(struct addrs}, sizeof(struct addrs});
addrs[index] .addr_y = phyYAddr;
addrs[index] .addr_cbcr = phyCAddr;
addrs[index] .buf_index = index;
if (mMsgEnabled & CAMERA MSG VIDEO FRAME) { II判斷有没有没定回调杯志
mDataCbTimestamp(timestamp, CAMERA_MSG_VIDEO FRAME,
buff-er, mCallbackCookie); / /友送视颜的预跪敷据到上展
J else {
mSecCamera->releaseRecordFrame(ind�x)· II_桴放Video敖据

�182
第9章 照相机系统 ooe

视颜的汞制敖据通迥特定的回调友送, 此姓的实瑰加入了时冏戳 (timestamp), 汞制


致据通常友送到媒体汞制器中,将迸行編碼。视颜呆制數据和预筑啟据都米自掇像失驱劫,
并且在硬件抽象屆中也用同一 結杓表示, 只是它亻i'J的去姓不同。

183�
第10章
OpenGL3D引擎

10.1 OpenGL系统概述

OpenGL (Open Graphics Library) 是 一 介杯准化的囹形渲染 (Render) 引擎,在 Android


中使用柝准的 OpenGL 接口作为 3D 部分的接口。在 Android 中已經具有了 OpenGL 的軟件
系统, 同时, 也提供了 OpenGL 移植扆的接口, 針対某介硬件的实現可以基于迏令移植尼
的接口实現 OpenGL 庠, 利用某秤驱劫程序机制和 Linux 內核乃至硬件(通常是灶理器內
的囹形单元)联系在一 起。 迏祥就可以实瑰 OpenGL 在某令硬件系统的囹形加速特性。
OpenGL 在 Android 本地扆的代碼之上, 具有 JNI 和 Java 尼, 其中 Java 尼使用 Java 柝
准的包作为接口 APJ, 提供給 Android Java 中的其他部分和 Java 皮用程序扆调用。

10.2 OpenGL系统的結枸

�10.2.1 OpenGL和OpenGL ES的柝准結杓


OpenGL 是行並颎域中最为「泛接納的 2D/3D 囹形 APL 其自诞生至今已催生了各秤
计算机平台及没各上的數千秤优秀座用程序。 OpenGL 袖立于视窗操作系统或其他操作系
统, 也是冏絡透明的。 在 CAD、 內容刨作、 能源、 娛示、 游戏玕友、 制造並、 制葯並及虛
姒現实等行並頲域中, OpenGL 帶助程序贝实現在 PC 、 工作站、 超级计算机等硬件没各上
的高性能、 极具沖士力的高视棠表現力囹形灶理軟件的升友。
OpenGL 的官方两站为: http://www.opengl.org/。
OpenGL 系统的姓理結杓如囹 10-1 所示。
从結杓上米看, OpenGL 系统分为几何璜熹 (vertices) 灶理、 囹元 (primitives) 灶理
和片元化 (fragments) 3 令這程。
几何预燕 (vertices) 灶理近程包含矩眸控制 (Matrix) 、光照 (Lighting) 、紋理 (Texture)
第10章 OpenGL 30引擎 ooe

仂调和严生、 求值(Evaluator)和璜煮(Vertex
Arrays)等內容。

Vertices •
五iii_i,:· · ······ ·· · · · · · · ·· ·--...... ·-···-丶
.,.•..
孕油 ; ! 1匡',······· ···-·. …..................
苫 & ..
Vert�A 麻庄, "''
... . .,,,,.. ·,..---.....
·""- �·. ····- ...................
;;; ; :1'
••
;:'令 .. . ... I : ; 虹即`嵓咧匾毚.
��ration ;.1 .i
-: ... ····•····一·、,·
ut . .
.· eo 誔 rs廊
co·..'. ,, ;
C: '

, 全,,r·····. '. 蝨訌:;.


.;. .,···...,. ,p•l' '...
:
· ...r、...,... "---·
弓�:.,: 臺 ! ··:: .
,. ··
Cf!ir苟1i·丶
'...
Vafues· : . •\

...•: ·:漢, . .• . ..... J,


.. l'·,.. "'元 ·'"Lrill'/
,, tlgh1J' 1:
. ··-�.. .
· 弓;·:這;
.
f , ... ,: 土一一
.....? ••,•••. ,
!
·�' '· ,
�,
·c: •
l I ..... ,;
',., ,

Primitives .. Fragments •

囹10-1 OpenGL系统的姓理結杓

囹元(primitives)姓理迥程包含视口(Viewport)、光栅化(Rasterization)、剪裁(Clipping)
和透视(perspective)等內容。
片元化(fragments)迥程包含材原貼囹(Texturing)、霎化(Fog)、反鋸齿(Antialiasing)、
片元化 (fragments)前外理 、 幀緩沖晁示和控制等內容。

- 提示:綬典的OpenGL是渲染引擎,其功能是单純的輸出昰示及其盅示的各秤效果,
不美注其他方面。

两令和OpenGL玕友相美的瓷料为OpenGL參考手冊和OpenGL編程向畀。
OpenGL參考手朋(藍节)的阿址为: http://www.glprogramming.com/blue/。
OpenGL編程向畀(紅节)的冏址为: http://www.glprogramming.com/red/。
OpenGLES (OpenGLfor Embedded Systems)是OpenGL三维囷形API的子集,針
対手机 、PDA和游戏主机等嵌入式没各而没计,OpenGLES
API由Khronos集困定又推「。
K虹onos是 一 介囷形欬硬件行北仂佘, 主要美注囹形和多媒体方面的升放杯准。
OpenGLES是免授枚費、跨平台、 功能完善的2D 和3D囹形成用程序接口API.針対
多神嵌入式系统考汀没计, 包括控制台、 移劫屯话、 手持没各 、 家屯没各和汽牟。 它由精
心定叉的桌面OpenGL子集組成, 刨造了軟件与囹形加速兼炅活強大的底屆交互接口。
OpenGLES包含浮熹运算和定熹运算系统描述以及EGL, 針対便挽没各的本地视窗系统規
范。OpenGL ES l .X面向功能固定的硬件没计, 并提供加速支持、 困形原量及性能杯准。

185�
•oo Android 板级支持与硬件相美子系统

OpenGLES 2.X 則提供了包括遮益器技木在內的全可編程 3D 囹形算法。 OpenGLES-SC 考


为有高安全性需求的特殊市汤精心打造。
OpenGLES 的两址是: http:/ /www.khronos.org/opengles/ 。
EGL (Native Platform Grap扣cs Interface, 本地囹形接口)是介于渲染 API 和本地平台


窗口 系统之 f司提供瓷源管理的可移植屄。通常工作于 OpenGLES 或者 OpenVG 的渲染接口
与平台本地窗口之阅。
EGL 与 OpenGLES 和 OpenVG 的美系如囹 10-2 所示。

penVG_ @LIES.

D
—'"�--屮華華戸
臺弓

G両llic5.c�n�引'Surface/buffer
EGL 1.. 2
圏10-2 EGL 与 OpenGLES 和 OpenVG的美系

EGL 目前最新版本是 1.2, 其阿站为: http:/ /www.khronos.org/egl/ 。

3 提示: 配合 EGL, OpenGL ES 可以元缝她移桂到不同的平台上, 例如 Windows 系


统、 Linux的X-Window等。

EGL 和 OpenGLES 接口配合使用的基本流程如下所示。


(1) 荻取 Display
要恙 eg!GetDispl 成)函數, 參數为EGL_DEFAULT_DISPLAY, 平台相美。
(2) 初始化 egl
要燕: egllnitiali 碸)函數, 得到版本另。
(3) 迸行 Config
要熹: eg!ChooseConfigO、eg!GetConfigs()函數, 參數癸型为 EGLConfig, 可以有多朴
造掙。
(4) 枸造 Surface
要意: eg!CreateWindowSurface() 函敖。

Surface 表示 介晁示囹扆, 可以米自硬件晁示內存, 也可以米自軟件位囹。
(5) 創建 Context
要燕: eglCreateConte刈)函數, 荻得上下文, 可以从中得到各秤狀态。
(6) 使用 OpenGL 函敖絵制
要 熹 : 迸入迏令阶段, 可 以调用 OpenGL 的各介函 數 , 需要昷示的时候调用
eglSwapBuffers()米晁示。

�10.2.2 忌体結枸
Android 中 OpenGL 系统的結杓如囹 10-3 所示。

, 186
第10章 OpenGL 30引擎 ooe

android .opengl Javax.microedition .khronos .opengles

Surface System
Java框架

OpenGL引擎
egl_nabve_wlndow_t libGLESv1 _CM
libEGL


..-一·一·
本地框架
Linux內核扆

囹 10-3 OpenGL系统結杓

自下而上, Android的OpenGL系统分成以下几今屆次。
( l) OpenGL的实現庠
OpenGL 的 实瑰庠 提 供 了杯 准 的接口, 其 中由Android自帶欬件庠 的名杯为
libGLES _android.so, 其代碼路往依然是frameworks/base/opengl/libs/libagl/。
如果需要枸建其他的OpenGL庠, 需要使用egl.cfg配置文件迸行配置, 并指定所使用
的OpenGL庠的路往。
(2) OpenGL的本地框架庠
OpenGL 本地框架庠的主要失文件 路往如下。
e frameworks/base/opengl/include/EGL。
e frameworks/base/opengl/include/GLES 。
e frameworks/base/opengl/include/GLES2 。
本地庠的代碼路往如下。
e frameworks/base/opengl/libs/EGL: 生成劫态庠 libEGL.so。
e frameworks/base/opengl/libs/GLE_CM: 生成劫态庠 libGLESv l _CM.so。
e frameworks/base/opengl/libs/GLES2: 生成幼态庠 libGLESv2 .so。
(3) OpenGL的JNI部分
OpenGL的JNI包括几令版本的EGL�的支持, 主要代碼路往为:
e frameworks/base/core/jni/android _opengl_GLES<version>.cpp。
其中<version>的值可以是10 、 lOExt 、 U 、 l l Ext和20, 迏些文件提供了OpenGL不同
版本的各介突的支持。
(4) OpenGL的Java 接口API
杯准的OpenGL的EGL奕和OpenGL ES的代碼路往为:
e frameworks/base/opengl/java/javax/microedition/khronos/egl/ 。

187�
•oo Android板级支持与硬件相美子系统

a frameworks/base/opengl/java/javax/microedition/khronos/opengles/。
迏是杯准的OpenGL癸,包含了GLlO.java 、 GLlOExt.java 、 GLl 1.java 、 GL11Ext.java 、
GLl l ExtensionPack.java、 GL.java等文件;在opengles中,主要的文件依然是 EGLlO.java。
Android通迥继承的方式实瑰OpenGL才示准的接口,代碼路往为:
a frameworks/base/opengl/java/corn/g oogle/android/gles」面。
迏部分 代 碼实瑰 com.g oogle.android.glesjni 包 中的各介癸,通迥继承实瑰杯 准 奕
javax.rnicroedition.khronos.opengles中的奕。
(5) OpenGL和Android系统結合的奕
与Android系统結合的部分的代碼路往为:frameworks/base/opengl/java/android/opengl/。
其中主要的炎调用了com.g oogle.android.glesjni包中的突和Android基絀GUI系统的
哭主要功能是实現 GLSurfaceView。 在Android 中, 为了实瑰 OpenGL 的杯准化, 使用
OpenGL ES的 Java杯准包 javax.rnicroedition.khronos.opengles作为接口,Android实現迏介

癸, 并和Andorid GUI尼的晁示机制联系在 起。

提示: Android 4.2的OpemGL的本地內容在frameworks/native/目汞中。

�10.2.3 OpenGL 庠的调用者


在opengl/libs/目汞中,实瑰了libEGL.so 、 libGLESvl _CM.so和libGLESv2.so 3介庠,
它亻i'J是OpenGL实珗庠的调用者。 其中,libGLESvl _CM.so和libGLESv2.so镱接并调用了
libEGL.so, 提供対OpenGLJNI部分的接口,而libEGL.so劫态打玕实現OpenGL庠, 并取
出符弓來使用 。
在libs/EGL 目汞中的 hooks.cpp文件中定乂的 egl_names就是各秤函敖的符另表:
#define EGL_ENTRY(_r, _api, ...) #_api,
char const * const egl_names[] = (
#include "egl_entries.in"
NULL

egl_entries.in中的內容和失文件 egl.h中定叉的 API是対皮的,只是写成了不同的形式,


其片段如下所示:
EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
EGL_ENTRY(EGLBoolean, egllnitialize, EGLDisplay, EGLint*, EGLint*)
EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)

举egl_entries.in中的內容包含在EGL_ENTRY中时,将自劫形成 令字符串數組, 敷
組取的就是函敖的名杯egl_names。
gl 方面的情況与之奕似,內容如下所示:
#define GL ENTRY( r, ap1., ... ) It ap1.,
char const * const gl_names[] = {
#include "entries.in"
NULL
);

�188
第10章 OpenGL 3D引擎 ooe

entries.in的內容和gl.h中定叉的API也是対皮的, 其片段如下所示:
GL ENTRY(void, glActiveTexture, GLenum texture)
GL二ENTRY(void, glAlphaFunc, GLenum func, GLclampf ref)
GL_ENTRY(void, glA),pha匣uncx, GLenum_ func, GLcl_ampx ref)

迏些內容也将形成由gl函數名組成的敖組, 名稔为gl_names。
libEGL.so首先将根据egl.cfg 定叉的內容找到OpenGL 的实瑰庠 , 迏部分也是在
loader.cpp中 实瑰的, 其ope瑱)函敷的相美片段如下所示:
void* Loader::open(EGLNativeDisplayType display, int impl, egl connection t* cnx)
void* dso;
char path[PATH_MAX];
int index.= int(display);
driver_t* hnd= 0;
const char* const format= "/system/lib/egl/lib氐s_%s.so'勺 II庠的路往
char const* tag= getTag(index, impl);
if (tag) {
snprintf(path, PATH_MAX, format, "GLES", tag);
dso= load_driver(path, cnx, EGL I GLESvl_CM I GLESv2);
if (dso) {
hnd= new driver_t(dso);
) else {
snprintf(path, PATH_MAX, format, "EGL", tag);
dso= load_driver(path, cnx, EGL);
if (dso) {
hnd= new driver_t(dso);
snprintf(path, PATH_MAX, format, "GLESvl_CM", tag);
hnd->set( load_driver(path, cnx, GLESvl_CM), GLESvl_CM );
snprintf(path, PATH_MAX, format, "GLESv2", tag);
hnd->set( load_driver(path, cnx, GLESv2), GLESv2 );


//省略錯课灶理部分內容
return (vo這*)hnd;

在迏里通迥调用getTag()解析/system/lib/egl/目汞中的egl.cfg文件, 荻取到劫态庠的名
稺然后调用load_driver()加戟OpenGL的实現庠 。
load_driver()实現的核心部分如下所示:
void *Loader::load_driver(const char* driver_absolute_path,
egl connection t* cnx, uint32 t mask) {
II省略錯课灶理部分內容
void* dso= dlopen(driver absolute_path, RTLD_NOW I RTLD_LOCAL);
II省略錯课灶理部分內容
if (mask & EGL) {
getProcAddress= (getProcAddressType)dlsym(dso, "eglGetProcAddress");
II省略錯课灶理部分內容
egl_t* egl= &cnx->egl;
_eglMustCastToProperFunctionPointerType* curr=
(_eglMustCastToProperFunctionPointerType*)egl;
char const * const * api= egl_names;
while (*api) I II循坏打升符另表
char const * name = *api;
eglMustCastToProperFunctionPointerType f= _.II取出庠中的符丹

189�
•oo Android板级支持与硬件相美子系统

( eglMustCastToProperFunctionPointerType)dlsym(dso, name);
II省略錯课灶理部分內容
*curr++ = f;
api++;

II省略, 通述getProcAddress调用荻得GLESvl_CM和GLESv2中的护展函敷
return dso;

load一如ver()的返回值是通迥dlopen()打升劫态庠返回的內容。在 load_如ver()后面, 通
迥getProcAddress调用荻得GLESv l _CM和GLESv2中的护展函數。 其中调用了 init_api()
函數用于根据名稔初始化各令接口。

10.3 OpenGL BSP的結枸

�10.3.1 移植的內容
OpenGL在Android系统的移植方面, 在不同的Android版本向, 存在OpenGL ES版
本不同的差昇和移植尼接口不同的差昇。 但是主要移植实現方面是基本相同的, 都包括以
下两介坏节。
驱劫程序: OpenGL硬件加速要与硬件交互, 因此需要在 Linux 內核中提供驱劫程
序米控制硬件。 在Linux中, OpenGL使用非杯准的驱劫程序接口。
圖硬件实現庠: 在用户空岡的OpenGL的实瑰庠。
在Android 2.0及之后的版本中,不同的OpenGL实現需要通迥的 egl.cfg文件迸行实現
庠的配置。
Android 中 OpenGL 的实現庠与 Android 系统接口方式是非杯准的, 但是其中实瑰的
OpenGL ES和EGL的接口都是杯准的。

�10.3.2 OpenGL移植展的接口

1. OpenGLES和EGL的柝准失文件
OpenGL ES 1.1版本的杯准失文件如下所示。
e gl.h: OpenGL ES 1.1主要失文件。
e glext.h: OpenGL ES 1.1护展染文件。
e glplatform.h: OpenGL的平台文件, 定又了OpenGL ES 1.1 平台相美的宏。
OpenGL ES2. l 版本的杯准失文件如下所示。
e gl2.h: OpenGL ES 2.0主要失文件。
e gl2ext.h: OpenGL ES 2.0护展失文件。
e gl2platform.h: OpenGL ES 2.0平台相美的宏。
EGL的杯准染文件如下所示。
e EGL/egl.h: EGL主要失文件。

, 190
第10章 OpenGL 30引擎 ooe

a EGL/eglext.h: EGL 护展失文件。


a EGL/eglplatform.h: EGL 平台相美的宏。
a KHR/khrplatform.h: 需要依賴岂前的 EGL 和 OpenGL ES� 文件。

2. Android 中的接口

在 Android 系统中, OpenGL 相芙的失文件路往如下所示。


e frameworks/base/opengl/include/GLES: OpenGL ES I. I 杯准失文件。
a frameworks/base/opengl/include/GLES2: OpenGL ES 2.0 杯准失文件。
e frameworks/base/opengl/include/EGL: EGL 柝准染文件。
a frameworks/base/opengl/include/KHR: EGL 平台相美染文件 khrplatform.h。

其中, frameworks/base/opengl/include/EGL 目汞中的 egl.h 定叉了 些函數, 它伯也是
EGL 才示准的定又, 如下所示:
EGLAPI EGLint EGLAPIENTRY eglGetError(void);
EGLAPI EGLDisplay EGLAPIENTRY eglGetDisplay(EGLNativeDisplayType display_id);
EGLAPI EGLBoolean EGLAPIENTRY eglinitialize(EGLDisplay dpy,
EGLint *major, EGLint *minor);
EGLAPI EGLBoolean EGLAPIENTRY eglTerminate(EGLDisplay dpy);
/*疍介函敖 */


egl.b 中提供的 eglGetProcAddress()的接口是 令特殊的函敖, 用于荻得各令函敖的地
址, 如下所示:
typedef void (*_eglMustCastToProperFunctionPointerType) (void);
EGLAPI _eglMustCastToProperFunctionPointerType EGLAPIENTRY
egl�etProcAddr�
一ss(const ch_ar *procn迎1e);

frameworks/base/opengl/include/EGL 目汞中的 eglplatform.h 文件为 Android 系统做出了


特殊修改, 內容如下所示:
#elif defined(ANDROID)
struct andro這native window t;
struct egl_native_pixmap_t;
typedef struct android native window t* EGLNativeWindowType;
typedef struct egl_nativ巴pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
telse
typedef EGLNativeDisplayType NativeDisplayType;
typedef EGLNativePixmapType NativePixrnapType;
回pedef EGL�� 旦,v色WindowType NativeWindowType;

NativeDisplayType 和 NativePixmapType 两介突型均定又成了 Android 中的哭型,


NativeWindowType 定又成了 void* 奕型的指針。

3. OpenGL 的实瑰方法

OpenGL 的实現庠需要实瑰杯准的 OpenGL ES 和 EGL 中定叉的各令接口,通這使用相


皮的驱劫程序米实現。 OpenGL ES 的接口 一 般都是杯准的, 而 EGL 作为移植扆, 其接口和
Android 系统的自身情況密切相美。

OpenGL 的实瑰庠中实現的函數必雍和原始的函敷名 致, 因为迏令庠是通迥劫态打

191�
•oo Android板级支持与硬件相美子系统

玕取出符另使用的。
EGL中的EGLConfig, EGLContext、EGLDisplay和EGLSurface的定又实际 上都是void
*奕型的指針。迏原本是 EGL杯准的定乂方式, 但是在实現迥程中,迏几介奕具体是什么
內容,坯需要和Android系统自 身的情況結合起米 。
eglplatform.h中 定叉的NativeDisplayType (本地 晁示癸型) 、NativeWindowType (本地
窗口突型) 和NativePixmapType (本地像素奕型),也需要使用本地的奕型替代。
根据以上的失文件定又, 在frameworks/base/include/ui/egl中的android_natives.h文件是
Android系统适配的EGL�文件。其中 定又了android_native_window_t奕, 通常就可以者做
EGL 中的NativeWindowType 奕米使用, 表示本地窗口的炎型。egl_native_pixmap_t 通常作
为NativePixmapType使用,表示本地像素奕型。但是NativeWindowType的奕型依然是void*。
eglGetProcAddress()用于荻得EGL护展庠的函數地址,也是OpenGL实瑰庠中 需要实
現的內容。

�10.3.3 OpenGL的调用和測试

在Android中OpenGL 可以通迥两秤方式调用: 神是通迥Java扆平台API调用米使

用, 在Android的GUI上 晁示其效果; 另 秤更为直接的方式是在本地 直接调用OpenGL
的API迸行操作, 直接利用实际的晁示没各作为榆出。元讠合是否經由硬件加速,二者經迥
的啟据 通道都基本相同。因此从OpenGL本身的运行來況,两神方式的差別并不大。
OpenGL的測试程序在frameworks/base/opengl/tests目汞中,包括以下內容。
e angeles、fillrate、filter、finish、gl2_basic、gl_basic、gralloc、lighting 1 709、血etex、
swapinterval、textures、tritex:其中的內容用于生成 命令行的可执行程序,不經迥Java
的GUI系统, 直接 在晁示没各上 迸行絵制。
e gl2」ava、gl2jni、gl」m、gldual、testPauseResume: 其中的內容各自生成座用程序
包( APK), 也用于測试 。
例如,fillrate中生成的可执行程序 test-opengl-fillrate 可以測试刷新的时向和幀率。 在
模姒器的命令行中执行,潿试結果如下所示:
# ./test-opengl-fillrate
w =320, h =480

62956418 1 62. 956418

"�
68274253 2

省略中I 司的內容
848728072 28
34.137127

30.311717
874744627 29 30.163608
911869970 30 30.395666
928407476 31 29. 948628

第 l列为絶対时阅( mano-sewnds), 第2列为測试次敖,第3列为平均消耗的时阅。

提示: 使用命令行測试OpenGL, 屏盅上将盅示渲染效果, 由于是直接写的盅示没


各,因此不受GUI上的按鈕等榆入的影南。 如果此时运行具有界面的反用,将覆蓋迷今
由OpenGL t俞出的結果,也可能出現二者輸出交替迸行的效果。

, 192
第10章 OpenGL 30引擎 ooe

10.4 OpenGL BSP的实現

Android系统包含了OpenGL的欬件 实瑰,針対每介硬件平台的实現可以是帶有硬件加
速的,垓部分通常需要使用 soc 內部的OpenGL加速单元,軟件通常以二迸制的形式提供。

�10.4.1 Android軟件OpenGL的实瑰
Android軟件 OpenGL的实現在目汞libagl/ 中, 其中Android.ml<文件的內容如下所示:
LOCAL_SHARED_LIBRARIES : = libcutils libhardware libutils libpixelflinger libETCl
LOCAL_LDLIBS : = -lpthread -ldl
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl

迏里各介源代硝文件将被編诵的幼态庠为libGLES_android.so, 需要指定放置在目才示系
统的system/lib/egl目汞中。

-提示: libGLES_android.so相蚩于Android l .x版本libagl.so。


在模姒器的实現中, system/lib/egl 目汞中只有 libGLES_android.so 令劫态庠, 作为
OpenGL默从的实現, 没有其他庠, 也没有egl.cfg 文件 。
libGLES_android.so庠中最主要的文件 为egl.cpp, 主要 实現了EGL接口中定乂的各介
函數。 其中定叉的egl一山splay_t結枸体如下所示:
const unsigned int NUM_DISPLAYS = l;
struct egl_display_t {
egl_display_t() : type(O), initialized(O) { }
static egl_display_t& get_display(EGLDisplay dpy);
static EGLBoolean is_valid(EGLDisplay dpy) {
return ((uintptr_t(dpy) 一 lU) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE;

NativeDisplayType type; II包含的第1介結杓, 表示本地晁示癸型


volatile int32 t initialized;
I;
static egl_display_t gDisplays[NUM_DISPLAYS);
egl丕lisplay_t& egl_display_t: :get_display(EGLDisplay dpy) { //函數实現
return gDisplays[uintptr_t(dpy)-lUJ;

egl_display_t 結 杓 体 实际上是EGLDisplay癸型在 实 現 中的 定 又 , 其 中包 含 了
Native DisplayType奕型。
egl_context_t結杓体的定又如下所示:
struct egl_context_t {
enum { IS_CURRENT = OxOOOlOOOO, NEVER_CURRENT = Ox00020000 };
uint32 t flags;
EGLDisplay dpy;
EGLConfig config;
EGLSurface read;
EGLSurface draw;
鮎g)_CO!.l,_te严:. context萁�GLfontezj: ctx) J

193�
•oo Android 板级支持与硬件相美子系统

ogles_context_t* const gl = static_cast<ogles_context_t*>(ctx);


return static_cast<egl_context_t*>(gl->rasterizer.base);

egl_context_t 結杓体实际上是 EGLContext 炎型在实瑰中的定又,其中皮垓包含灶理迥


程中所有的上下文, 迏里使用了几令成吳來表示。
egl_surface_t 結杓体的定又如下所示:
struct egl_surface_t {
enurn { PAGE_FLIP = OxOOOOOOOl, MAGIC = Ox31415265};
uint32_t magic;
EGLDisplay dpy;
EGLConfig config;
EGLContext ctx;
//省略函教部分的定又

egl_surface_t 結杓体实际上是 EGLSurface 奕型的实現, 表示一令 EGL 晁示的界面。


所实現的 eglGetDisplayO函數如下所示:
EGLDisplay eglGetDisplay(NativeDisplayType display} {
II省略部分內容
if (display== EGL_DEFAULT_DISPLAY} { II EGL_DEFAULT_DISPLAY == 0
EGLDisplay dpy= (EGLDisplay}l;
egl_display_t& d= egl一中splay_t::get_display(dpy}; II 返回 gDisplays [ 1-lUJ
ct.type= display; II egl_display t 中的 NativeDisplayType
return dpy;

return EGL NO DISPLAY; // EGL NO DISPLAY== 0

eglGetDisplay()的返回实际上是一 今句柄,由下 一 介步骕作为參數亻耜送。 eglGetDisplay()


的下 一 今步驟通常是 egllnitialize() 。之后的 EGL 初始化近程, 坯需要經历 eglGetConfigs() 、
eglCreateWindowSurface() 、 eglCreateContext()等函敖。
EGL 中几介重燕函數的內容如下所示:
EGLBoolean eglinitialize(EGLDisplay dpy, EGLint *major, EGLint *minor){}
EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
NativeWindowType window, const EGLint *attrib_list) {}
EGLBoolean eglGetConfigs( EGLDisplay dpy, EGLConfig *configs,
EGLint config_size, EGLint *num_config){}
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
EGLContext share list, const EGLint *attrib_list) {}
EGLBoolean eglSwapBuff_e経(_§GLDisplay_J:i_p_Y, EGLSurface draw) {}

egl.cpp 将迏些函敖依次实瑰, 美于其參敖的哭型, 在函敖的內部使用的都是实际的結


杓体, 在外部使用的則 一 般都是 void* 奕型的指針。
egl.cpp 中, eglGetProcAddress 函數的定又如下所示:
void (*eglGetProcAddress (const char *procname))() {
extention_map_t const * const map = gExten訌onMap;
for (uint32_t i=O ; i<NELEM(gExtentionMap) ; i++) (
if (!strcmp(procname, map[i].name)) {
return map[i] .address; //荻取每二企指針的地址

�194
第10章 OpenGL 3D 引擎 ooe

return NULL;


eglGetProcAddress 的返回奕型实际上是 介函敖指針, 迏里使用元參數, 返回值奕型
为void 的函數, 即 egl.h 中定叉的_eglMustCastToProperFunctionPointerType 奕型, 可以特
換成其他格式的函數指針炎型使用。
struct extention_map_t {
const char * const name;
_eglMustCastToProperFunctionPointerType address;

gExtentionMap 表示名稔到函數指針的映射, gExtentionMap 是迏介奕型的數組定又


EGL 护展庠的函敖, 其片段的內容如下所示:
static const extention_map_t gExtentionMap[J = {
{ "glDrawTexsOES",
(_eglMustCastToProperFunctionPointerType)&glDrawTexsOES),
{ "glDrawTexiOES",
( eglMustCastToProperFunctionPointerType)&glDrawTexiOES),
{ "glDrawTexfOES",
( eglMustCastToProperFunctionPointerType)&glDrawTexfOES },
II省略部分函敖指針內容
-
.
-
'
-

�10.4.2 Nexus One系统的实瑰


Nexus One 系统基于高通 QSD 8x 系列的灶理器, 它集成了 Adreno 的囹形芯片。 其目
才示系统的/system/lib/egl 目呆的內容如下所示:
# ls /system/lib/egl
libq3dtools_adreno200.so
libGLESv2_adreno200.so
libGLESv-1 CM adreno200.so
libGLES android.so
libEGL_adreno200.so
eg1_.cfg

其中 egl.cfg 文件的內容如下所示:
# cat /system/lib/egl/egl.cfg
0 0 android
0辶CM_adreno200

迏里使用的 adreno200 表示的是 Nexus One 的高通 QSD 8x 內部 OpenGL 引擎的


adreno200 。 根据 egl.cfg 文 件中的 內 容, 可 以找到三介相美的幼态庠米使 用,
libq3dtools_adreno200.so 是 一 介鋪助的工具庠。

�10.4.3 Nexus S系统的实珉


Nexus S 系统基于三星 S5PCXXX 系列的姓理器, 它集成了 Power VR SGX530 的圏形
芯片。 其目杯系统的/system/lib/egl 目呆的內容如下所示:

195�
• O O Android 板级支持与硬件相美子系统

$ ls /system/lib/egl
libGLESv2 POWERVR SGX530 121.so
libGLESvl ' CM POWERVR SGX530 ' 121.so
libGLES android.so
libEGL POWERVR SGX530 121. so
egl.cfg

其中 egl.cfg 文件的內容如下所示:
$ cat /system/lib/egl/egl.cfg

送里使用的 POWERVR_SGX530_ l 2l 表示的是三星灶理器的囹形灶理器 SGX530 的名


稔。 此囹形灶理器也同祥被其他的姓理器所使用。

�10.4.4 Galaxy Nexus系统的实瑰


Galaxy Nexus 的 OMAP4460 灶理器使用了 PowerVR SGX540 的囹形芯片, 与 Nexus S
的癸似, 但是速度更快, 在目才示系统中的各令部分以二迸制的形式提供, 一般包含在
extr act- imgtec-maguro.sh 朐本中, 可以从 Google 的冏站直接得到。
在目杯系统的/vendor/lib/egl 目呆中,具有几令庠: libEGL_POWERVR_SGX540_120.so、
libGLESvl CM POWERVR SGX540 120.so、libGLESv2 POWERVR SGX540 120.so 。 在
Android 4.x 中, 內容可以被 OpenGL 的核心直接使用。
另外,在目才示系统的/vendor/lib/中坯有几介与 PVR 相失的幼态庠,也和 OMAP 灶理器
的晁示系统有美系, 而晁示系统也和 Open GL 相美。

�196
第ll章
OpenMax引擎

11.1 OpenMax 系统概述

OpenMax是一 令多媒体皮用程序的框架杯准。 其中, OpenMax IL C集成扆)技木靚格


定叉了媒体組件接口, 以便在嵌入式器件的流媒体框架中快速集成加速編解碼器。
在Android中, OpenMax IL扆通常可以用于多媒体引擎的插件, Android多媒体引擎
都可以使用OpenMax作为插件, 主要用于編解碼(Codec)灶理。
在Android框架扆, 也定又了由Android封裝的OpenMax接口, 和杯准的接口概念基
本相同, 但是使用C++奕型的接口, 并且使用了Android的Binder IPC机制。 Android封裝
OpenMax的接口直接被Android各介本地展程序调用, 例如StageFright多媒体引擎。
Android的OpenMax系统的相失內容如表11-1所示。

表11-1 Android的OpenMax系统的相夫內容
Android 的展次 OpenMax 系统部分 描 述

硬件尼次 主要为編解碼硬件 一般米自 soc 硬件的支持


操作系统扆 自定乂的驱劫程序 没有杯准的接口

本地展 OpenMax fL 扆柝准实瑰、 Android 框架中的 OpenMax 两介扆次均可以被调用

使用者 多媒体引擎 例如 StageFngbt

11.2 OpenMax 子系统結枸

�11.2.1 OpenMax系统的結杓

1. OpenMax忌体扆次結杓
OpenMax是一 介多媒体皮用程序的框架杯准, 由NVIDIA公司和Kbronos在2006年
推出。 OpenMax是元授枚費的, 跨乎台的皮用程序接口APL 通迥使媒体加速組件能移在
•oO Android 板级支持与硬件相美子系统

玕友、 集成和編程坏节中实現跨多操作系统和灶理器硬件平台, 提供全面的流媒体編解碼


器和皮用程序便挑化。
OpenMax 的官方冏站是: http://www.khronos.org/openmax/。
OpenMax 实际上分成三介居次,自下而上分別是 OpenMax DL (玕友屄)、 OpenMaxIL
( 集成扆) 和 OpenMaxAL (皮用尼)。


OpenMax 的三介扆次如囹 11-1 所示。

�一��-
邸ia <IPPluioiw - be written Application

c丑
po<tably. indopenclont "'虹
underly國....i;. pl

今笠己在*
Platfoon lledia Framework
I
QerlMAxllL Defines·�甩 :.,�誌
r
�rnterlaceo
I I

"'""'"國 medypouc-i呣

c譯 ds C譁c" c譯护
Modio -- ... be wlillon'!lino

�°E三��
�forportabillly ..,_-..
parallel ond•心oilcon arch
C)perMA)(jDL,


邸ia Engines - CPUs, OSP, Hardware Accelerato,s etc

penMAX layers can be im emented together or


r
independently from he other layers

囷11-1 OpenMax 的三今尼次

第 -尼: OpenMax DL (Development Layer, 升友扆)


OpenMax DL 定又了一 介 API, 它是音颜、 视颜和囹像功能的集合。 硅供皮商能哆在

介新的姓理器上实現并优化, 然后編解碼供皮商使用它米編写更「泛的編解碣器功能。
包括音頻信弓的姓理功能, 如 FFT 和 filter; 囹像原始灶理, 如颜色空阅若換、 视颜原始姓
理, 以实現例如 MPEG-4、 H.264、 MP3 、 AAC 和 JPEG 等編解碼器的优化。
第二尼: OpenMax IL (Integration Layer, 集成扆)
OpenMaxIL 作为音頻、视頻和囹像編解碼器能与多媒体編解碼器交互,并以统一的行
为支持組件(例如瓷源和皮朕)。 迏些編解碼器或讠午是軟硬件的混合体, 対用户是透明的底
扆接口皮用于嵌入式、 移劫没各。 它提供了皮用程序和媒体框架。 S 編解碼器供皮商必須
写私有的或者封閉的接口, 集成迸移幼没各。 IL 的主要目的是使用特征集合为編解碼器提
供 一 令系统抽象, 解決多令不同媒体系统之同桎便性的祠題。
第三扆: OpenMaxAL (Appliction Layer, 皮用扆)

OpenMaxALAPI 在皮用程序和多媒体中向件之冏提供了 令杯准化接口,多媒体中伺
件提供服各以实瑰被期待的 API 功能。
OpenMaxAPI 将佘与灶理器 一同提供, 以使庠和編解碼器升岌者能夥高速有效地利用
新器件的完整加速潜能, 元颎担心其底尼的硬件結杓。 垓杯准是針対嵌入式没各和移劫没
各的多媒体欬件架杓。 在架杓底屄为多媒体的編解碼和敖据灶理定又了一 套统一 的編程接
口, 対多媒体敖据的姓理功能迸行系统级抽象, 为用户屏蔽了底昆的细节。 因此, 多媒体
皮用程序和多媒体框架通迥 OpenMax IL 可以以 一 神统一的方式米使用編解碼和其他多媒

, 198
第U章 OpenMax 引擎 ooe

体敖据灶理功能, 具有跨越欬硬件平台的移植性。

提示:在实际亙用中, OpenMax 的三木扆次中使用較多的是 OpenMax IL 集成屋, 由


于操作系统到硬件的差并和多媒体亙用的差并, OpenMax 的 DL 和 AL 屋使用相対較少。

2. OpenMax IL展的結杓

OpenMax IL 目前已經成为事实上的多媒体框架杯准。嵌入式灶理器或者多媒体編解碼
模玦的硬件生严者, 通常提供杯准的 OpenMax IL 尼的欬件接口, 迏祥欬件的升友者就可
以基于迏令扆次的柝准化接口迸行多媒体程序的升友。
OpenMax IL 的接口展次結枸适中,既不是硬件編解碼的接口, 也不是皮用程序尼的接
口, 因此比絞容易实瑰柝准化。
OpenMax IL 的扆次結杓如囹 11-2 所示。

三三 Application
[

Multimedia Middleware
(e.g. OpenMAX AL, Native Framework)

。 penMAX Integration Layer

Component Component Component


Interface interface Interface

Codec

I I I I
OpenMAX Development Layer

DL Primitives DL Primitives

囷11-2 OpenMax IL的尼次結杓

囹中虛紱中的內容是 OpenMax IL 尼的內容, 主要实瑰了 OpenMax IL 中的各令組件


(Component)。 対下晨 OpenMax IL 可以调用 OpenMaxDL 扆的接口, 也可以直接调用各
神 Codec 实現。対上尼, OpenMax IL 可以給 OpenMaxAL 屄等框架扆 (Middleware) 调用,
也可以給皮用程序直接调用。
OpenMax IL 主要內容如下所示。
.客户端 (Client): OpenMax IL 的调用者。
圖組件 (Component): OpenMax IL 的单元, 每 一 介組件实現一 神功能。
圖端口 (Port): 組件的喻入/榆出接口。

199�
eoo Android板级支持与硬件相美子系统

.隧道化(Tunneled): 辻两介組件直接连接的方式。
OpenMaxIL的基本运作迥程如囝11-3所示。

LOient

MAlmeciaF�

e8爻至
8
Non-Tumeled

AcoeleratOf

"'
IPC. "
Siril
Coll1X)lllll1t
IPC
Corrmurication
1-laloMn
h:a!/etatad
C亟

Proprietary
Com面ication

囹11-3 OpenMax IL 的基本运作近程

如囹11-3所示, OpenMALIL的客户端通迥调用4介OpenMALIL組件, 实現了 一介


功能。4介組件分別是 Source組件、 Host組件、 Accelerator組件和Sink組件。 Source組件
只有 一介諭出端口;而Host組件有 一介榆入端口和一令諭出端口; Accelerator組件具有 一
令榆入端口, 调用了硬件的編解碼器,加速主要体現在迏介坏节上。Accelerator組件和Sink
組件通這私有通信方式在內部迸行连接, 没有經近明确的組件端口。
OpenMAL IL在使用时, 其數据流也有不同的姓理方式: 既可以經由客户端, 也可以
不経由客户端。囹11-3 中, Source組件到Host組件的啟据流就是經迥客户端的; 而Host
組件到Accelerator組件的敖据流就没有經迥客户端, 使用了隧道化的方式; Accelerator組
件和Sink組件甚至可以使用私有的通信方式。
OpenMax Core是輔助各介組件运行的部分, 通常需要完成各介組件的初始化等工作,
在真正运行這程中, 重燕的是各令OpenMax IL的組件, OpenMax Core不是重熹, 也不是
杯准。

OpenMAL IL的組件是OpenMax IL实現的核心內容, 令組件以榆入/榆出端口为接

口, 端口被连接到另 介組件上。 外部対組件可以友送命令, 坯可以迸行没置/荻取參數、
配置等內容。組件的端口可以包含緩沖匡(Buffer)的臥列。
組件灶理的核心內容是: 通迥榆入端口消耗Buffer, 通迥榆出端口填充Buffer, 多組
件相联接可以杓成流式的姓理。
OpenMALIL 中 一介組件的結杓如囷11-4所示。
組件的功能和其定叉的端口奕型密切相美,通常情況下:只有 一令諭出端口的为Source
組件;只有 一介喻入端口的为Sink組件;有多令諭入端口、 一介輸出端口的为Mux組件;
有 一介諭入端口、 多令諭出端口的为DeMux 組件;諭入/榆出端口各 一令組件的为中向灶
理坏节, 迏是最常見的組件。

�200
第 11 章 OpenMax 引擎 ooe

IL Client
Or other
component

Bui區Sent

招J 1-4 OpenMAL IL中 一 介組件的結杓

端口具体支持的數据也有不同的炎型。 例如, 対于 一 介諭入/諭出端口各 一 令組件, 其


諭入端口使用 MP3 格式的數据, 榆出端口使用 PCM 格式的數据, 那么迏介組件就是 一 介
MP3 解碼組件。
隧道化 (Tunneled) 是 一介哭于組件连接方式的概念。 通迥隧道化可以将不同組件的

令諭入端口和 一 介諭出端口连接到 一 起, 在迏秤情況下, 两令組件的姓理迥程合并, 共
同姓理。 尤其対于单榆入和单榆出的組件, 两介組件将作为奕似 一介使用。

�11.2.2 Android中OpenMax的适配居
Android 中 的 OpenMax 适配扆的代碼 路往为: frameworks/base/include/media/ 。 在
Android 4.2 中, 相皮的路往为: frameworks/av/include/media/。
OpenMax 的失文件是 IOMX.h, IOMX 哭表示 一介 OpenMax 的底扆实現。 其中 IOMX
炎的主要內容如下所示:
class IOMX : public !Interface {
public:
DECLARE META INTERFACE(OMX);
typedef void *buffer_id;
typedef void *node_id;
virtual bool livesLocally(pid_t pid) = 0;
struct Componentinfo (String8 mName; List<String8> mRoles;); II組件的信息
virtual status_t listNodes(List<Componentinfo> *list) = 0; II节魚列表
virtual status_t allocateNode(const char *name,
const sp<IOMXObserver> &observer, node_id *node) = 0; II分配节焦
virtual status_t freeNode(node_id node) = 0; II找到节焦
virtual status_t sendCommand( II没送命令
node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = O;
virtual status_t getParameter( node_id node, II荻得參致
OMX_INDEXTYPE index, void *params, size_t size) = 0;


virtual status_t setParameter(node_id node,
MX_INDEXTYPE index, const void *params, size_t size) = 0;
virtual status_t getConfig( node_id node,
II没匱參敷

II荻得配置
OMX_INDEXTYPE index, vo這*params, size_t size)= O;
virtual status_t setConfig(node_id node, II没箕配翌

201�
•oo Android 板级支持与硬件相美子系统

OMX_INDEXTYFE index, const vo,id *params, size_t size) = 0;


virtual status_t useBuffer( node_id node, II使用緩沖巨
OMX_U32 port」.ndex, const sp<IMemory> &params, buffer id *buffer) = 0;


virtual status_t allocateBuffer(node_id node,
MX_U32 port_index, size_t size,
buffer_id *buffer, void **buffer_data) = 0;
//分配緩沖巨

virtual status_t allocateBufferWithBackup(node_id node, //分配帶后各緩沖巨


OMX_U32 port_index, const sp<IMemory> &params, buffer id *buffer) = 0;
virtual status_t freeBuffer(node_id node, //秤放緩沖巨
OMX_U32 port_index, buffer_id buffer) = 0;
virtual status t fillBuffer(node id node, buffer id buffer) = O; //填充緩沖巨
virtual status_t emptyBuffer( node_id node, //消耗緩沖巨
buffer_id buffer, OMX_U32 range_offset,
OMX_U32 range_length, OMX_U32 flags, OMX_TICKS timestamp) = 0;
virtual status_t getExtensionindex(node_id node,
const char *parameter_name, OMX_INDEXTYPE *index) = 0;
I I ...... 省略其他內容刨建渲染器(从ISurface, 从Surface, 从Java屋)
);


IOMX 表示的是 OpenMax 的 介組件 , 根据 Android 的 Binder IPC 机制, BnOMX 继
承 IOMX, 实現者需要继承实現 BnOMX 。 IOMX 炎中, 除了和柝准的 OpenMax 的
GetParameter 、 SetParameter 、 GetConfig 、 SetConfig, SendCommand 、 UseBuffer 、 AllocateBuffer 、
FreeBuffer、 FillThisBuffer 和 EmptyThisBuffer 等接口外, 坯 包含了創造渲染器的接口
createRenderer(), 創建的接口为 IOM刃lenderer 癸型。
IOMX 中坯有几令刨建渲染器的函敷如下所示:
virtual sp<IOMXRenderer> createRenderer( II刨建渲染器(从!Surface)
const sp<ISurface> &surface, const char *componentName,
OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth,
size_t encodedHeight, size_t displayWidth, size t displayHeight) = 0;
sp<IOMXRenderer> createRenderer( //創建渲染器(从Surface)

。const sp<Surface> &surface, const char *componentName,


MX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth,
size_t encodedHeight, size_t displayWidth, size_t displayHeight);
sp<IOMXRenderer> createRendererFromJavaSurface( //从Java展創建渲染器
JNIEnv *env, jobject javaSurface, const char *componentName,
OMX_COLOR_FORMATTYPE colorFormat, size_t encodedWidth,

其中, 只有第 一 介 createRendererO 函數是純虛函敖, 第二介 createRendererO 函敖和



createRendererFromJavaSurfaceO通迥调用第 介 createRendererO函敖实珗。

IOM蹕enderer 炎表示 介 OpenMax 的渲染器, 其定又如下所示:
class IOMXRenderer : public !Interface (
public:
DECLARE 'META,.INTERFACE(OMXRenderer);
virtual void render(IOMX::buffer_id buffer) = O; II渲染輸出函敷

IOMXRenderer 只包含一介 render 接口, 其參敖奕型 IOMX::buffer_id 实际上是 void*,


根据不同的渲染器使用不同的突型。
在 IOMX.h 文件中, 另有表示戏察器奕的 IOMXObserver, 迏令奕表示 OpenMax 的戏
察者, 其中只包含一介 o卟fossage()函數, 其參數为 omx_message 接口体, 其中包含 Event

, 202
第11章 OpenMax引擎 ooe

事件 奕型、 FillThisBuffer完成和EmptyThisBuffer完成几秤奕型。

-提示: Android中OpenMax的适配屋是OpenMAX IL扆之上的封裝扆, 在Android


系統中放StageFright调用, 也可以被其他部分调用。

11.3 OpenMax BSP 的絹枸


在Android中实現OpenMax IL尼 和杯准的OpenMax IL展的方式基本相同, 一般需要
实瑰以下两令坏节。
龕編解碼驱幼程序:位于Linux內核空岡, 需要通迥Linux內核调用驱劫程序, 通常
使用非杯准的驱劫程序。
e OpenMax IL扆: 根据OpenMax IL扆的杯准 失文件实現不同功能的組件。

�11.3.1 OpenMax IL展的接口


OpenMax IL尼的接口定又由若干介失文件 組成,提供了实瑰者的接口, 它亻I']的基本描
述如下所示 。
e OMX_Types.b: OpenMax Il的啟据奕型定乂。
e OMX_Core.h: OpenMax IL 核心的API。
e OMX_Component.b: OpenMax IL 組件相美的 API。
e OMX_Audio.h: 音颜相失的常量和啟据結杓。
e OMX_IVCommon.b: 囷像和视颜公共的常量和數据結杓。
e OMX_Image.b: 囷像相美的常量和敖据結杓。
e OMX_Video.h: 视颜相美的常量和啟据結杓。
e OMX_Otber.b: 其他數据結杓(包括A/V同步)。
e OMX_Index.h: OpenMax IL 定叉的敖据結杓索引。
e OMX_ContentPipe.b: 內容的管道定叉 。

提示: OpenMax杯准只有失文件, 没有杯准的庠, 改置没有定乂函敖接口。 対于实


珗者, 需要实現的主要是包含函教指針的結杓体。

其 中, OMX_Component.h中定叉的OMX_COMPO NENTTYPE結杓体是OpenMax.IL

扆的核心內容 , 表示 介組件, 其內容如下所示:


typedef struct OMX_COMPONENTTYPE {


MX_U32 nSize; //迏介結杓体的大小 */


MX_VERSIONTYPE nVersion;
MX_PTR pComponentPrivate;
//版本兮 */
//迏介組件的私有敖据指針. */


II调用者(IL client)没置的指針, 用于保存它的私有數据, 伟回給所有的回调函敷 */
MX_PTR pApplicationPrivate;
II以下的函數指針返回OMX core. h中的対I翌內容可
OMX_ERRORTYPE (*GetComponentVersion) ( II荻得組件的版本可
OMX_IN OMX_HANDLETYPE hComponent, OMX_OUT OMX_STRING pComponentName,
OMX_!}UT OMX_V_I! 聮江O啞'Y_PE* pCompon�ntVersio_n�,

203�
•oo Android板级支持与硬件相美子系统

OMX OUT OMX VERSIONTYPE* pSpecVersion,


OMX_ERRORTYPE {*SendCommand) { //友送命令刃
OMX IN OMX HANDLETYPE hComponent, OMX IN OMX COMMANDTYPE Cmd,
OMX IN OMX COMMANDTYPE Cmd, OMX IN OMX U32 nParaml,


OMX IN OMX PTR pCmdData);
MX ERRORTYPE (*GetParameter) ( //荻得參數 */
OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nParamindex,
OMX_INOUT OMX_PTR pComponentParameterStructure);
OMX ERRORTYPE (*SetParameter) ( //没覧參敷 */

。 。
OMX IN
MX_IN
OMX HANDLETYPE hComponent, OMX IN
OMX_PTR pComponentParameterStructure);
OMX INDEXTYPE nindex,


MX ERRORTYPE (*GetConfig) ( //荻得配置
MX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_INDEXTYPE nindex,
OMX_INOUT OMX_PTR pComponentConfigStructure);
OMX ERRORTYPE (*SetConfig) ( //没置配置
OMX IN OMX HANDLETYPE hComponent, OMX IN OMX INDEXTYPE nindex,
OMX_IN OMX_PTR pComponentConfigStructure);
OMX_ERRORTYPE (*GetExtensionindex) ( II特換成OMX結杓的索引
OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_STRING cParameterName,
OMX_OUT OMX_INDEXTYPE* pindexType);


OMX ERRORTYPE
MX_IN
OMX ERRORTYPE
(*GetState) (
OMX_HANDLETYPE hComponent,
(*ComponentTunnelRequest) (
II荻得組件圭前的狀态
OMX_OUT OMX STATETYPE* pState);
//用于连接到另

介組件


OMX_IN OMX_HANDLETYPE hComp, OMX_IN OMX_U32 nPort,
MX_IN OMX_HANDLETYPE hTunneledComp, OMX_IN OMX_U32 nTunneledPort,
OMX_INOUT OMX_TUNNELSETUPTYPE* pTunnelSetup);
OMX ERRORTYPE (*UseBuffer) ( //为某介端口使用Buffer
OMX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_IN OMX_U32 nPortindex,
OMX_IN OMX_PTR pAppPrivate, OMX_IN OMX_U32 nSizeBytes,
OMX_IN OMX_U8* pBuffer);

。。
OMX ERRORTYPE (*AllocateBuffer) (
MX_IN OMX_HANDLETYPE hComponent,
MX_INOUT OMX_BUFFERHEADERTYPE** ppBuffer,
//在某介端口分配Buffer

OMX_IN OMX_U32 nPortindex,


OMX_IN OMX_U32 nPortindex, OMX_IN OMX_PTR pAppPrivate,
MX_IN OMX_U32 nSizeBytes);
OMX ERRORTYPE (*FreeBuffer) ( //将某介端口Buffer秤放
OMX IN OMX HANDLETYPE hComponent, OMX_IN OMX_U32 nPortindex,
OMX IN OMX BUFFERHEADERTYPE* pBuffer);
OMX ERRORTYPE {*EmptyThisBuffer) { II辻組件消耗迏1'Buffer
OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);

。。
OMX ERRORTYPE
MX_IN
MX_IN
(*FillThisBuffer) (
OMX_HANDLETYPE hComponent,
OMX_BUFFERHEADERTYPE* pBuffer);
II辻組件填充迏介Buffer

OMX ERRORTYPE (*SetCallbacks) ( II没置回调函敷


OMX_IN OMX_HANDLETYPE hComponent,
OMX_IN OMX_CALLBACKTYPE* pCallbacks, OMX IN OMX PTR pAppData);
OMX ERRORTYPE (*ComponentDeinit) ( II反初始化組件
OMX_IN OMX_HANDLETYPE hComponent);


OMX_ERRORTYPE (*UseEGLimage) (
MX_IN OMX_HANDLETYPE hComponent,
OMX_INOUT OMX_BUFFERHEADERTYPE** ppBufferHdr, OMX_IN OMX_U32 nPortindex,
OMX_IN OMX_PTR pAppPrivate, OMX_IN void* eglimage);


OMX_ERRORTYPE (*ComponentRoleEnum) (
MX_IN OMX_HANDLETYPE hComponent, OMX_OUT OMX_U8 *cRole,
OMX IN OMX U32 nindex);
) OMX COMI'ONENTTYPE;

�204
第 11章 OpenMax引擎 ooe

OMX_COMPONENTTYPE 結杓体实現后, 其中的各令函敖指針就是调用者可以使用


的內容。 各介函數指針和 OMX_core.h 中定叉的內容相対皮。
EmptyThisBuffer 和 FillThisBuffer 是基本机制,前者辻組件消耗緩沖巨,表示対成組件
諭入的內容;后者辻組件填充緩沖巨, 表示対皮組件榆出的內容。
UseBuffer、 AllocateBuffer 和 FreeBuffer 为与端口 相美的緩沖巨管理函數, 対于組件的
端口有些可以自己分配緩沖匡, 有些可以使用外部的緩沖巨, 因此有不同的接口対其迸行
操作。
SendCommand 表示向組件笈送控制炎的命令。 GetParameter、 SetParameter、 GetConfig
和 SetConfig 接口用于輔助的參數和配置的没置和荻取。
ComponentTunnelRequest 用于組件之同的隧道化连 接, 其中需要指定两介組件及其相
连的端口。
ComponentDelnit 用于組件的反初始化。

3 提示: OpenMax 函救的參故中, 綬帝包含 OMX_IN 和 OMX_OUT 等宏, 他仞的实


际內容为空, 只是为了杯记參教的方向是揄入述是輸出。

OMX_Component.h 端口突型的定叉为 OMX_PORTDOMAINTYPE 枚举奕型, 內容如


下所示:


typedef enum OMX_PORTDOMAINTYFE {
MX_FortDomainAudio,
OMX_FortDomainVideo,
//音颜哭型端口
//视颜哭型端口


OMX_FortDomainimage,
MX_FortDomainOther,
//圏像哭型端口
//其他突型端口
OMX_PortDomainKhronosExtensions = Ox6FOOOOOO,
OMX FortDomainVendorStartUnused = Ox7FOOOOOO
OMX FortDomainMax = Ox7ffffff
) OMX FORTDOMAINTYPE;

音颜癸型、 视颜炎型、 囹像奕型和其他哭型是 OpenMax IL 尼次所定叉的 4 秤端口的


奕型, 表示 端口使用哪神癸型的啟据。
端 口具体內 容的定又使 用 OMX PARAM PORTDEFINITIONTYPE 癸(也在
OMX_Component.h 中定叉)來表示, 其內容如下所示:
typedef struct OMX PARAM PORTDEFINITIONTYPE {
OMX U32 nSize; II結杓体大小
OMX VERSIONTYPE nVersion; //版本
OMX_U32 nPortindex; //端口弓
OMX DIRTYPE eDir; //端口的方向
OMX U32 nBufferCountActual; II为迏介端口实际分配的Buffer的教目


OMX U32 nBufferCountMin;
MX_U32 nBufferSize;
OMX_BOOL bEnabled;
//迏介端口 Buffer的最小致目
//緩沖巨的字节敷
//是否使能
OMX_BOOL bPopulated; //是否在填充
OMX PORTDOMAINTYPE eDomain; //端口的突型
union { //端口实际的內容, 由突型确定具体結杓
OMX AUDIO PORTDEFINITIONTYPE audio;
OMX VIDEO PORTDEFINITIONTYPE video;
OMX IMAGE PORTDEFINITIONTYPE image;
OMX OTHER PORTDEFINITIONTYPE other;

205�
• O O Android 板级支持与硬件相美子系统

J format;
OMX_BOOL bBuffersContiguous;
OMX_U32 nBufferAlignment;
} OMX''PARAM. PORTDEFINITIONTYPE;

対于 一 介端口, 其重燕的內容如下。
• 端口的方向 (OMX_DIRTYPE): 包含 OMX_DirInput C 諭入)和 OMX_DirOutput (榆
出)两秤。
0端口分配的緩沖巨敷目和最小緩沖巨數目。
圖端口的奕型 (OMX_PORTDOMAINTYPE): 可以是 4 秤奕型。
國端口格式的敷据結杓: 使用 format 联合体來表示, 具体由4 秤不同癸型來表示, 与
端口的哭型相対囹。
OMX_AUDIO _FORTDEFINITIONTYPE 、 OMX_VIDEO_PORTDEFINITIONTYPE 、
OMX_IMAGE_PORTDEF吋TIONTYPE和OMX_OTHER_PORTDEFINITIONTYPE等几介
具体的格式突型, 分別在 OMX_Audio.h 、 OMX_Video.b 、 OMX_Image.h 和 OMX_Other.h
迏4 今失文件中定又。
OMX_BUFFERHEA DERTYPE 是在 OMX_Core.h 中定叉的, 表示緩沖巨的失部結杓。
OMX_Core.h 中定叉的枚举癸型 OMX_STATETYPE 命令表示 OpenMax 的狀态机, 內
容如下所示:
typedef enum OMX STATETYPE {
OMX_Stateinval這, II組件監測到內部的致据結杓被破坏

。。
OMX_StateLoaded,
MX_Stateidle,
II組件被加敦但是没有完成初始化
II組件初始化完成, 准各升始


MX_StateExecuting, II組件接受了玕始命令, 正在灶理敷据


MX_StatePause, II組件接受暫停命令
MX_StateWaitForResources, II組件正在等待瓷源
OMX_StateKhronosExtensions = Ox6FOOOOOO, II保留
OMX_StateVendorStartUnused = Ox7FOOOOOO, II保留
OMX StateMax = OX7FFFFFFF
} OMX STATETYPE;

OpenMax 組件的狀态机可以由外部的命令改変, 也可以由內部友生的情況改変。


OpenMax IL 組件狀态机的迁移美系如囹 11-5 所示。

跗l l-5 OpenMax IL組件狀态机的迁移美系

�206
第 n章 OpenMax引擎 ooe

OMX_Cor e.h中定叉的枚举奕型OMX_COMMA1叩TYPE表示対 組件的命令癸型, 內


容如下所示:


typedef enum OMX_COMMANDTYPE {
MX_CommandStateSet, //改変狀态机器
OMX_CommandFlush, II刷新敷据臥列
OMX_CommandPortDisable, //禁止端口


OMX_CommandPortEnable, II使能端口


MX_CommandMarkBuffer, //杯记組件或Buffer用于戏察


MX_CommandKhronosExtensions = Ox6FOOOOOO, II保留
MX_CommandVendorStartUnused = Ox7FOOOOOO, II保留
OMX CommandMax = OX7FFFFFFF
) OMX COMMANDTYPE;

OMX_COMM,心DTYPE奕 型 在SendCommand调 用 中 作 为參致被使 用 , 其 中


OMX_CommandStateSet就是改変狀态机的命令。

�11.3.2 Android的OpenMax
Android系统的一 些部分対OpenMax IL尼迸行使用, 基本使用的是 杯准OpenMax IL
扆的接口, 只是迸行了简单封裝。 杯准的OpenMax IL 实現很容易以 插件的形式加入到
Android系统中。
Android中使用的主要是OpenMax的編解硝功能。 呈然OpenMax也可以生成 諭入/諭
出、 文件解析—枸建等組件, 但是在各介系统(不仅是 Android)中使用最多的坯是 編解碼
組件。 媒体的諭入/榆出 坏节和系统的美系很大, 引入OpenMax柝准比絞麻煉; 文件解析-
杓建 坏节 一 般不需要使用硬件加速。 編解碼 組件也 是最能体現硬件加速的坏节, 因此最常
使用。
Android的多媒体引擎可以使用OpenMax作为多媒体 編解碼的插件, 只是没有直接
使用OpenMaxIL扆提供的純C语言接口, 而是対其迸行了 一定的封裝。
Android中坯提供了OpenMax的适配扆 接口, 対OpenMax IL的杯准 組件迸行 封裝 适
配,作为 Android本地扆的接口,
可以被 Android的多媒体引擎调用。 Android中OpenMax
适配尼的接口在frameworks/base/include/media/目汞中的IOMX.h文件定叉。

11.4 OpenMax BSP的实現

�11.4.1 OpenMax IL实瑰的內容


対于OpenMax IL尼的实瑰, 一 般的方式并不调用OpenMax DL居。 具体实瑰的內容
就是 各令不同的組件。 OpenMaxIL 組件的实現包含以下两介步驟。

Cl) 組件的初始化函數:硬件和OpenMax敖据結杓的初始化, 般分成函數指針初始
化、 私有數据結杓的初始化、 端口的初始化等几介步骕, 使用其中的 pComp
onentP r ivate
成昃保留本 組件的私有數据为上下文, 最后荻得填充, 完成OMX_COMPONENTTYPE癸
型的結枸体。

207�
•oo Android板级支持与硬件相美子系统

(2)OMX_ COMPONENTTYPE炎型結杓体的各令指針: 实現其中的各介函敷指針,


需要使用私有啟据时,从其中的 p ComponentPrivate得到指針,特化成实际的數据 結枸使用。
端口的定又是OpenMaxIL組件対外部的接口。OpenMaxIL常用的組件大都是榆入和
榆出端口各 一介。対于最常用的編解碼( Codec)組件,通常需要在每介組件的实現近程中,
调用硬件的編解碼接口來实珗。 在組件的內部灶理中,可以建立紱程米灶理。OpenMax組
件的端口有默从參數,但也可以在运行时没置,因此 一介端口也可以支持不同的編碼格式。
音颜編碼組件的輸出和音颜編碼組件的榆入通常是原始敖据格式(P CM 格式), 视颜編碼
組件的喻出和视颜編碼組件的榆入通常是原始敖据格式(YUV格式)。

3 提示:在一秤特定的硬件实現中, 編解碼部分具有相似性, 因此通常可以枸建一今




OpenMax鈕件的 基美 或者公共函敖, 來完成公共性的操作。

�11.4.2 OMAP3 的 OpenMax IL 实瑰的結枸和机制

1. OMAP 3 OpenMax IL实瑰的結杓和机制


Android 的玕源代碼中, 已經包含了TI 的 OpenMax IL屄的实現代碼, 其路往为:
hardware/ti/omap3/omx/。
其中包含的主要目呆如下所示。
e system: OpenMax核心和公共部分。
e audio: 音颜灶理部分的OpenMaxIL組件。
e video: 视颜灶理部分OpenMaxIL組件。
e image: 囹像灶理部分OpenMaxIL組件。
在TIOpenMaxIL实現中,最上面的內容是OpenMax的管理者用于管理和初始化,中
阅尼是各介編解碼单元的OpenMax IL杯准組件,下展是 LCML展, 供各介OpenMax IL
杯准組件所调用。
TIOpenMax IL实瑰的公共部分在system/src/openmax_ii/目汞中, 主要內容如下。
e ornx_core/src: OpenMax IL的核心, 生成劫态庠libOMX_ Core.so。
e lcrnl/: LCML的工具庠, 生成劫态庠lib LCML.so。
TIOpenMaxIL实瑰的結杓如囷11-6所示。
TIOpenMaxIL的视颜(Video)相美的組件在video/src/openmax_ill目汞中, 主要內容
如下所示。
e prepost_processor: Video 數据 的前灶理和后灶理, 生成劫态庠libOMX.TI.VPP.so。
e video_decode: Video解碼器, 生成劫态庠libOMX.TI.Video.Decoder.so。
e video_encode: Video 編碼器, 生成劫态庠libOMX.TL Video .encoder.so。
TIOpenMax IL的音颜(Audio)相美的組件在audio/src/openmax_i I/目呆中,主要內容
如下所示。
e g7l l _dec: G刀I解碼器, 生成媯态庠libOMX.TI.G71l .decode.so。
e g71l_enc: G刀I 編碼器, 生成劫态庠libOMX.TI.G7 l l .encode.so。

, 208
第11章 OpenMax引擎 ooe

OpenMax IL的杯准实珗

libOMX_Core (Tl OpenMax IL核心)

各介
組件

LCML (Linux Common Multimedia Layer)

DSP Bridge (Omap灶理器的DSP拼)

DSP Bridge驱劫程序

囝11-6 TI OpenMax IL实現的結杓

e g722_dec: G722解碼器,生成劫态庠libOMX.TI.G722.decode.so 。
• g722_enc: G722編碼器,生成劫态庠libOMX.TI.G722.encode.so。
a g726_dec: 0726解碼器,生成幼态庠libOMX.TI.0726.decode.so。
a g726_enc: G726編碼器,生成劫态庠libOMX.TI.G726.encode.so。
a g729_dec: G729解碼器,生成劫态庠libOMX.TI.G729.decode.so 。
e g729_enc: G720編碼器,生成幼态庠libOMX.TI.G729.encode.so 。
e nbamr_dec: AMR窄帶解碼器,生成劫态庠libOMX.TI.AMR.decode.so 。
e nbamr _enc: AMR窄帶編碼器,生成劫态庠libOMX.TI.AMR.encode.so。
e wbamr_dec: AMR寛帯解碼器,生成劫态庠libOMX.TI.WBAMR.decode.so。
e wbamr _enc: AMR寛帶編碼器,生成幼态庠libOMX.TI.WBAMR.encode.so。
e mp3_dec: MP3解碼器,生成劫态庠libOMX.TI.MP3.decode.so。
e aac_dec: AAC解碼器,生成幼态庠libOMX.TI.AAC.decode.so 。
e aac_enc: AAC編碼器,生成幼态庠libOMX.TI.AAC.encode.so 。
e wma_dec: WMA解碼器,生成幼态庠libOMX.TI.WMA.decode.so。
TI OpenMax IL的囹像(Image)相芙的組件在image/src/openmax_ii/目汞中,主要內容
如下所示。
e jpeg_enc: JPEG編碼器,生成劫态庠libOMX.Tl.JPEG.Encoder.so。
e jpeg_dee: JPEG解碼器,生成劫态庠libOMX.Tl.JPEG.decoder.so。

2. 核心和公共內容

LCML的全稔是"Linux Common Multimedia Layer", 是TI的Linux公共多媒体扆。

209�
•oo Android 板级支持与硬件相美子系统

在 OpenMax IL 的实現中,送介內容在 system/src/opemnax_il/lcml/目呆中, 主要文件是子目


呆 src 中的 LCML_DspCodec.c 文件。 通迥调用 DSPBridge 的內容,iJ: ARM 和 DSP 迸行通
信, 然后 DSP 迸行編解碼方面的灶理。 DSP 的运行坯需要固件的支持。
TI OpenMax IL 的 核 心实 瑰 在 system/src/opemnax_il/omx_core/ 目汞中 , 生成 TI
OpenMax IL 的核心庠 libOMX_Core.so。
其中, 子目呆 src 中的 OMX_Core.c 为主要文件, 定叉了編解碼器的名稔等, 其片段
如下所示:
char *tComponentName[MAXCOMP) [2) = {
Jpeg"}, // 00像和视颜編解碼器
{"OMX.TI.JPEG.decoder", "image decoder.·
{"OMX.TI.JPEG.Encoder", "image_encoder.jpeg"),
{"OMX.TI.Video.Decoder", "video_decoder.avc"},
{"OMX.TI.Video.Decoder", "video_decoder.mpeg4"},
{"OMX.TI.Video.Decoder", "video_decoder.wmv"},
{"OMX.TI.Video. encoder", "video_encoder.mpeg4"},
{"OMX.TI.Video.encoder", "video_encoder. h263"},
{"OMX.TI.Video.encoder兀 "video_encoder.avc"},
II省略语音相栄組件
#ifdef BUILD WITH TI AUDIO //音颜編解碼器
{"OMX.TI.MP3.decode", "audio_decoder.mp3"},
{"OMX.TI.AAC.encode", "audio_encoder.aac"},
{"OMX.TI.AAC.decode", "audio_decoder.aac"},
{"OMX.TI.WMA.decode", "audio_decoder.wma"},
{"OMX.TI國BAMR.decode", "audio_decoder.amrwb"},
{"OMX.TI.AMR.decode", "audio_decoder.amrnb"},
{"OMX. TI.AMR.encode", "audio_encoder.amrnb"},
{"OMX.TI.WBAMR.encode", "audio encoder.amrwb"},
#endif
{NULL, NULL),
};

tComponentName 數組的各介璜中,第一 令表示編解碼庠內容,第二令表示庠所实現的


功能, 即稔为职靑 (rule) 的部分。
TIOMX_GetHand園)函致用于荻得各令組建的句柄, 实瑰的主要片段如下所示:
OMX_ERRORTYPE TIOMX_GetHandle( OMX_HANDLETYPE* pHandle, OMX_STRING cComponentName,
OMX_PTR pAppData, OMX_CALLBACKTYPE* pCallBacks) {
static const char prefix[) = "lib";


static const char postfix[] = ".so";
MX ERRORTYPE (*pComponentinit)(OMX HANDLETYPE*);
OMX_ERRORTYPE err = OMX_ErrorNone;
OMX_COMPONENTTYPE *componentType;
const char* pErr = dlerror();
//省略錯课赴理內容
int i = O; //下面准各循坏査找組件
for (i=O; i< COUNTOF(pModules); i++) { if (pModules[i] == NULL) break; }
//省略借·课姓理內容
int refindex = 0;
for (refindex= O; refindex < MAX_TABLE_SIZE; refindex++) {
//循坏査找組件列表
if {strcmp(componentTable[refindex] .name, cComponentName) == 0) {
if (componentTable[refindex] .refCount >= MAX_CONCURRENT_INSTANCES) {
//省略錯课灶理內容
} else {

�210
第U章 OpenMax 引擎 ooe

char buf(sizeof(prefix) + MAXNAMESIZE + sizeof(postfix));


strcpy(buf, prefix);
strcat(buf, cComponentName);
strcat(buf, postfix);
pModules[i) = dlopen(buf, RTLD_LAZY I RTLD_GLOBAL);
//省略錯课灶理內容
II劫态取出初始化的符另
pComponentinit = dlsym(pModules[i), "OMX_Componentinit");
pErr = dlerror();
II省略錯课灶理內容
*pHandle = malloc(sizeof(OMX_COMPONENTTYPE));
//省略錯课姓理內容
pComponents[i) = *pHandle;
componentType = (OMX_COMPONENTTYPE*) *pHandle;
componentType->nSize = sizeof(OMX_COMPONENTTYPE);
err = (*pComponentinit)(*pHandle); II 执行初始化工作
II省略部分內容

err = OMX_ErrorComponentNotFound;
goto UNLOCK_MUTEX;
II 省略部分內容
return (err);

在TIOMX_ GetHandle()函敖中,根据tComponentName敷 組中劫态庠的名稔,劫态打


升各令編解碼实現的劫态庠,取出其中的OMX_Componentlnit符弓执行各介組件的初始化。

3. 一 -1'OpenMax IL組件的实瑰
TI OpenMax IL中各令組件都是通迥调用LCML米实瑰的,实現的方式基本癸似。主
要都是实珗了名稔为OMX_Componentlnit 的初始化函數,实現 OMX_COMPONENTTYPE
奕型的結杓体中的各令成贝。各介組件的目汞 結杓和文件結杓也突似。
以MP3解碼器的实瑰为例,在audio/src/opemnax_il/mp3_dec/src 目汞中,主要包含以
下文件。
a OMX_Mp3Decoder.c: MP3解碼器組件实珗。
a OMX_Mp3Dec_CompThread.c: MP3解碼器組件的紱程循坏。
a OMX_Mp3Dec_Utils.c: MP3解碼器的相美工具,调用LCML 实瑰真正的 MP3解碼
的功能。
OMX_Mp3Decoder.c 中的 OMX_Component!血)函數奐靑組件的初始化,返回的內容
再从參敷中得到,迏介函數的主要片段如下所示:
OMX_ERRORTYPE OMX_Componentinit (OMX_HANDLETYPE hComp)

OMX ERRORTYPE eError = OMX ErrorNone;
OMX_COMPONENTTYPE *pHandle = (OMX_COMPONENTTYPE*) hComp;
OMX PARAM PORTDEFINITIONTYPE *pPortDef l.. p = NULL, *pPortDef op = NULL;

。。
OMX AUDIO PARAM PORTFORMATTYPE *pPortFormat = NULL;
MX_AUDIO_PARAM_MP3TYPE *mp3_ip = NULL;
MX一、AUDIO PARAM PCMMODETYPE *mp3 op = NULL;
MP3DEC_COMPONENT_PRIVATE *pComponentPrivate = NULL;
MP3D_AUDIOJ!EC_PORT_TYPE *pCompPort = NULL;

211�
• 0 O Android板级支持与硬件相美子系统

MP3D_BUFFERLIST *pTemp = NULL;


int i=O;
MP3D_OMX_CONF_CHECK_CMD(pHandle,1,l); //检査命令
//省略初始化OMX_COMPONENTTYPE癸型的指針pHandle
OMX_MALLOC_GENERIC(pHandle->pComponentPrivate,MP3DEC COMPONENT PRIVATE);
pComponentPrivate = pHandle->pComponentPrivate; //私有指針互相指向
pComponentPrivate->pHandle = pHandle;
//省略初始化私有指紂pComponentPrivate
II没咒榆入端口(OMX_PARAM_PORTDEFINITIONTYPE突型)的默从值
pPortDef_ip->nSize = sizeof(OMX PARAM PORTDEFINITIONTYPE);
pPortDef_ip->nPortindex = MP3D INPUT PORT;
pPortDef ip->eDir = OMX_Dirinput;
pPortDef ip->nBufferCountActual = MP3D NUM INPUT BUFFERS;
pPortDef ip->nBufferCountMin = MP3D NUM INPUT BUFFERS;
pPortDef_ip->nBufferSize = MP3D INPUT BUFFER SIZE;
pPortDef ip->nBufferAlignment = DSP CACHE ALIGNMENT;
pPortDef_ip->bEnabled = OMX_TRUE;
pPortDef_ip->bPopulated = OMX_FALSE;
pPortDef_ip->eDomain = OMX PortDomainAudio;
pPortDef_ip->format.audio.eEncoding = OMX_AUDIO_CodingMP3;
pPortDef_ip->format.audio.cMIMEType = NULL;
pPortDef_ip->format.audio.pNativeRender = NULL;
pPortDef ip->format.audio.bFlagErrorConcealment = OMX_FALSE;
//没鸞榆出端Q (OMX_PARAM_PORTDEFINITIONTYPE癸型)的默从值
pPortDef_op->nSize = sizeof(OMX PARAM PORTDEFINITIONTYPE);
pPortDef_op->nPortindex = MP3D OUTPUT PORT;
pPortDef_op->eDir = OMX DirOutput;
pPortDef_op->nBufferCountMin = MP3D_NUM_OUTPUT_BUFFERS;
pPortDef_op->nBufferCountActual = MP3D_NUM_OUTPUT_BUFFERS;
pPortDef_op->nBufferSize = MP3D OUTPUT BUFFER SIZE;
pPortDef op->nBufferAlignment = DSP CACHE ALIGNMENT;
pPortDef_op->bEnabled = OMX_TRUE;
pPortDef_op->bPopulated = OMX_FALSE;
pPortDef_op->eDomain = OMX PortDomainAudio;
pPortDef_op->format.audio.eEncoding = OMX_AUDIO_CodingPCM;
pPortDef_op->format.audio.cMIMEType = NULL;
pPortDef_op->format.audio.pNativeRender = NULL;
pPortDef op->format.audio.bFlagErrorConcealment = OMX_FALSE;
II省略分配端h
//没猩榆入端口的默从格式
pPortFormat = pComponentPrivate->pCompPort[MP3D_INPUT_PORT)->pPortFormat;
OMX_CONF_INIT_STRUCT(pPortFormat, OMX_AUDIO_PARAM_PORTFORMATTYPE);
pPortFormat->nPortindex = MP3D INPUT PORT;
pPortFormat->nindex = OMX_IndexParamAudioMp3;
pPortFormat->eEncoding = OMX AUDIO CodingMP3;
//没咒榆出端口的默从格式


pPortFormat = pComponentPrivate->pCompPort[MP3D_OUTPUT_PORTJ->pPortFormat;
MX_CONF_INIT_STRUCT(pPortFormat, OMX_AUDIO_PARAM_PORTFORMATTYPE);
pPortFormat->nPortindex = MP3D OUTPUT PORT;
pPortFormat->nindex = OMX IndexParamAudioPcm;
pPortFormat->eEncoding = OMX AUDIO CodingPCM;
II省略部分內容
eError = Mp3Dec_StartCompThread(pHandle); //启劫MP3解碼鈛程
II省略部分內容
return eError;

送介組件是OpenMax的杯准实現方式, 対外接口的內容只有 一 介初始化函數。 完成

�212
第 n章 OpenMax引擎 oo•

OMX_COMPONENTTYPE哭型的初始化。榆入端口的編另为MP3D_INPUT_PORTC=O),
炎型为OMX_PortDomainA呻O, 格式为OMX_AUDIO_CodingMP3。 諭出端口的編弓是
MP3D_OUTPUT_PORT (=l ), 奕型为OMX_PortDoma皿\.udio, 格式为OMX_AUDIO_
CodingPCM。
OMX_Mp3Dec_CompThread.c 中定乂了MP3DEC_ComponentThread()函敖, 用于創建
MP3解碼 紱程的执行函敖 。
OMX_Mp3Dec_Utils.c中的Mp3Dec_StartCompThre琿)函數, 调用了POSIX的紱程庠
建立MP3解碼的紱程, 如下所示:
nRet = pthread_create (&(pComponentPrivate->ComponentThread), NULL,
MP3DEC_ComponentThread, pC�omponentPrivate)_;

Mp3Dec_StartCompThread()函敖就是在組件 初始化函敷OMX_Componentlnit()最后调
用的內容。 MP3 紱程的升始并不表示解碼迥程的升始, 紱程需要等待通迥pipe机制荻得命
令和啟据CcmdPipe和dataPipe), 在适蚩的时候玕始工作 。 迏介 pipe在MP3解碼組件的
SendCommand等实瑰写操作,在紱程中波取其 內容。

�11.4.3 OMAP4 的 OpenMax IL 实瑰

1 . OMAP 4 OpenMax IL的基本結杓

OM战4 OpenMax IL即Ducati OpenMax, 简稔DOMX, 其代碼路往为hardware/ti/


omap 4xxx/domx, 子目琭和其中的內容如下所示。
e domx: 表示DOMX的核心部分, 生成劫态庠libdomx.so。
e mm_osal: 操作系统扆, 生成劫态庠libmm_oscl.so。
.。mx_core: 表示DOMX的核心部分, 生成libOMX_Core.so。
e omx_proxy_component: 包含了几介組件的子目汞。
Ducati_binary: 二迸制的固件, 放置于目才示系统的etc/fumware/ducati-m3.bin路往 。
omx_proxy_component中包括若干介組件的子目汞。
·。mx_sample: 示例組件, 生成libOMX.TI.DUCATII .MISC.SAMPLE.so。
e omx_camera: 照相机組件, 生成libOMX.TI.DUCATll .VIDEO.CAMERA.so。
e omx_video_dee: 视颜解碼組件, 生成libOMX.Tl.DUCATI I .VIDEO.DECODER.so。
e omx_h264_enc: H264 編碼組件, 生成libOMX.TI.DUCATI l .VIDEO.H264E.so。
e ornx_mpeg4_enc: MPEG4 編碼組件,生成libOMX.TI.DUCATil.VIDEO.MPEG4E.so。

2. OMAP 4 OpenMax IL的核心部分


。mx_core/src中的OMX_Core.c文件是 OpenMax的核心部分, 其中定乂了各介組件的
职靑,在tComponentName敖組中, 內容如下所示:
char *tComponentName(MAXCOMP) (MAX_ROLES) = { // MAXCOMP==SO, MAX_ROLES==20
{"OMX.TI.DUCATil.VIDEO.DECODER", "video_decoder.mpeg4",
"video_decoder.avc", "video_decoder.h263", "video_decoder.wmv",
"video_decoder.vp6", "video_decoder.vp7", NULL},
{"OMX.T_I.DUCATil.VIDEO.DECODER. secure", '�video_deco且�r.mpeg4",

213�
•oO Android 板级支持与硬件相美子系统

"video_decoder.avc", "video_decoder.h263", NULL},


{"OMX. TI.DUCATil.VIDEO.H264D", "video_decoder.ave", NULL},
{"OMX.TI.DUCATil.VIDEO.H264E", "video_encoder.ave", NULL},
{"OMX.TI.DUCATil. VIDEO .MPEG4D", "video_decoder.mpeg4", NULL},
{"OMX.TI.DUCATI1.VIDEO.MPEG4E", "video_encoder.mpeg4",
,'video_encoder.h263", NULL},
{"OMX.TI.DUCATil.VIDEO.VP6D", "video_decoder. vp6", NULL},
{"OMX.TI. DUCATil.VIDEO. VP7D", "video_decoder.vp7", NULL},
{ '. 。MX.TI.DUCATil.IMAGE. JPEGD", "jpeg_decoder. jpeg", NULL},
{"OMX.TI. DUCATil.VIDEO.CAMERA", "camera.omx", NULL},
{NULL, NULL}, //表示組件的結束
};

在 OpenMax 的 OMX_lnitO 函敖中调用了 OMX_BuildComponentTableO 函敖,在其中注


朋了 tComponentName 數組。 迏令致組的每 一 元素也是 一令效組, 其中組件名稔作为第 一
介元素, 职靑作为后面的元素, 可以有多今职贤, 以 NULL 为結束。

3. 一介 OpenMax IL 緝件的实瑰

OpenMax 的各介組件的实現在 omx_proxy_component 目呆中,都遵从柝准的 OpenMax


IL 尼的結杓, 实現的情況奕似。
Video 解碼的实現在 omx_video_dec/src/omx_proxy_videodec.c 中, 其初始化內容的主
体部分如下所示:

。。
#define COMPONENT_NAME "OMX.TI.DUCATil.VIDEO.DECODER"
MX_ERRORTYPE OMX_Componentinit(OMX_HANDLETYPE hComponent)
MX_ERRORTYPE eError = OMX_ErrorNone;
{

OMX_COMPONENTTYPE *pHandle = NULL;


PROXY_COMPONENT_PRIVATE *pComponentPrivate = NULL;
pHandle = (OMX_COMPONENTTYPE *) hComponent;
pHandle->pComponentPrivate =(PROXY_COMPONENT_PRIVATE *)
TIMM_OSAL_Malloc(sizeof(PROXY_COMPONENT PRIVATE), TIMM_OSAL_TRUE,
0, TIMMOSAL MEM SEGMENT INT); II建立私有敖据的句柄
pComponentPrivate =
II組件的私有組件
(PROXY_COMPONENT_PRIVATE *) pHandle->pComponentPrivate;
TIMM_OSAL_Memset(pComponentPrivate, 0, sizeof(PROXY_COMPONENT_PRIVATE));
pComponentPrivate->cCompName =
TIMM_OSAL_Malloc(MAX_COMPONENT_NAME_LENGTH * sizeof{OMX_UB),
TIMM_OSAL一TRUE, 0, TIMMOSAL_MEM_SEGMENT_INT);
eError = OMX_ProxyViddecinit(hComponent); II调用Video解碼的初始化
EXIT:
return eError;

OpenMax IL 組件的 FillThisBuffer 函數的实現如下所示:


OMX_ERRORTYPE PROXY_VIDDEC_FillThisBuffer(OMX_HANDLETYPE hComponent,
MX_BUFFERHEADERTYPE * pBufferHdr) {
OMX_ERRORTYPE eError = OMX_ErrorNone, eCompReturn = OMX_ErrorNone;
RPC_OMX_ERRORTYPE eRPCError = RPC_OMX_ErrorNone;
PROXY_COMPONENT_PRIVATE *pCompPrv;


OMX_COMPONENTTYPE *hComp = (OMX_COMPONENTTYPE *) hComponent;
MX_U32 count = 0;
IMG_native_handle_t* grallocHandle; II用于晁示榆出的Gralloc的句柄
OMX PARAM PORTDEFINITIONTYPE sPortDef;
PROXY_require(pBufferHdr != NULL, OMX_ErrorBadParameter, NULL);
PROXY___!"equire(hComp->pComponentPrivate != NULL,

�214
第 n章 OpenMax引擎 ooe
OMX_ErrorBadParameter, NULL);
PROXY_CHK_VERSION(pBufferHdr, OMX_BUFFERHEADERTYPE);
pCompPrv = (PROXY_COMPONENT_PRIVATE *) hComp->pComponentPrivate;
if(pCompPrv->proxyPortBuffers[OMX_VIDEODECODER OUTPUT PORT] .proxyBufferType
= = GrallocPointers) { //根据视颜榆出端口迸行灶理
grallocHandle = (IMG_native_handle_t*)pBufferHdr->pBuffer;
sPortDef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
sPortDef.nVersion.s.nVersionMajor = OMX_VER_MAJOR;
sPortDef.nVersion.s.nVersionMinor = OMX_VER_MINOR;
sPortDef.nVersion.s.nRevision = OxO;
sPortDef.nVersion.s.nStep = OxO;
sPortDef.nPortindex = OMX_VIDEODECODER_INPUT_PORT;
eError = PROXY_GetParameter(hComponent,
OMX_IndexParamPortDefinition,&sPortDef);
pCompPrv->grallocModule->lock((gralloc_module_t const *)
pCompPrv->grallocModule, // Gralloc模坎的內容
(buffer_handle_t)grallocHandle, GRALLOC_USAGE_HW_RENDER,
0,0,sPortDef.format.video.nFrameWidth,
sPortDef.format.video.nFrameHeight,NULL);

eRPCError = PROXY_FillThisBuffer(hComponent, pBufferHdr);


EXIT: DOMX_EXIT("eError: %d", eError);
return eError;

此姓的视颜解碼組件实际上已經迸行和Android系统相美的灶理, 対奐靑晁示的
Gralloc做出了相皮的没置, 因为解碼的大小和晁示大小是相美的。

215�
第12章
位玦隻制

12.1 位坎岌制概述

位玦夏制(copybit)是Android中一 令提供了可以加速內存囹形灶理的加速模坎。 主
要包括玦夏制和位囝拉伸两部分功能。根据其參數的不同, 也可以实現旋特、 透明度混疊、
颜色格式特換等方面的功能。
位玦夏制是一介可造实珗的部件, 如果其下展可以使用加速硬件米实現, 其硬件抽象
展是Android中一 令杯准的硬件模玦, 可以被Android 系统的各介本地部分调用。 Java扆
和位玦夏制系统元接蝕。 位玦夏制模玦是一介加速坏节, 本身的实現是可造的, 灶于比絞
底尼的部分, 只能被本地部分调用, 没有直接向上尼Oava扆)提供接口。 copybit实現,
SurfaceFlinger中令対其有所调用, 也可以在平台特定的本地展程序中自己调用。

- 提示: copybit郎分为較早期的加速解決方案, Android4.x已綬没有copybit部分。

12.2 位坎夏制子系统緒枸

�12.2.1 忌体結杓
Android 中的位坎夏制系统是 一 介可造的子系统, 由用于加速的驱幼程序 和硬件抽
象尼組成, Android的各介部分都可以按照调用硬件模玦的方式调用。

在Android系统默从的框架中, 対位玦夏制部分的调用都有 后各方案", 即在系统没
有位坎夏制模玦时, 采用軟件的方法灶理。 在某令特定的硬件平台, 实瑰了位坎夏制模玦
后, 可以被自己实現的其他本地所调用。
Android中的位坎夏制系统的結枸如囹12-1所示。
第12章 位玦隻制 ooe

位坎及制调用者 SurfaceFlinger

L王三旦言言/三/\\
囹 12-l 位坎夏制系统的結杓

位玦夏制系统由驱劫程序、 硬件抽象扆和调用者迏几部分組成。
(l) 驱劫程序: 使用各秤自定叉的驱劫程序。
(2) 硬件抽象尼的接口是 copybit 硬件模坎, 代碼路往为:
�hardware/libhardware/include/hardware/copybit.h
Android 中柝准的硬件模坎, 实瑰后将生成名稔为 copybit. <platform>.so 的劫态庠, 放
置在目杯文件系统/system/lib/hw/ 目汞中。
(3) copybit 的调用者
在 Android 系统中,位玦夏制系统主要被与囹形灶理相美的系统调用,在默从的 Android
实現中, 奐贤囹扆管理的 SurfaceFlinger 庠调用了位玦夏制模玦。

�12.2.2 copybit的调用者
copybit 模玦在 Android 中主要的调用者是 Su.rfaceF恤ger, 此外OpenGL 庠的軟件实現
也调用了 copybit 模玦。
SurfaceFlinger 调用的部分在 frameworks/base/surfaceflinger/ 目汞的 LayerBuffer.cpp 文件
中。 打升模玦部分的代碼如下所示:
if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) (
copybit_open(module, &mBlitEngine);

以上操作通迥打玕硬件模玦和 copybit 没各得到 rnBlitEngine, 其奕型为 copybit_device_t


結杓体的指針。
copybit 主要的调用部分在 LayerBuffer 的 onDrawO 函數中, 如下所示:
VO這LayerBuffer::BufferSource::onDraw(const Region& clip) const

sp<Buffer> ourBuffer(getBuffer());
status t err = NO ERROR;
NativeBuffer src(ourBuffer->getBuffer());
const Rect transformedBounds (mLayer. getTransformedBounds ());
hf defined(EGL一ANDROID_image_native_buffer)
if (mLayer. mFlags & DisplayHardware: :DIRECT_TEXTURE) {
err = INVALID OPERATION;
if (ourBuffer->supportsCopybit()) {
copybit device t* copybit = mLayer.mBlitEngine; II荻得copybit引擎
if _(coe_y9._it && err ,L= NO_ERROR) {

217�
•oo Android 板级支持与硬件相美子系统

err = initTempBuffer(); //第 一 次的时候要刨建EGLimageKHR


if (err == NO_ERROR) {
const NativeBuffer& dst(mTempBuffer);
region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, OxFF);
copybit->set_parameter(copybit, COPYBIT_DiTHER, COPYBIT_ENABLE);
err = copybit->stretch(copybit, &dst.img, &src.img,
&dst.crop, &src.crop, &clip); //迸行OO像拉伸功能
if (err != NO一ERROR) { clearTempBufferimage();}

#endif
//省略部分內容: 如果没有copybit的灶理方式

以上參敷没置表示 copybit 的參數为没有変形 (COPYBIT_TRANSFORM)、没有 Alpha


混疊 (COPYBIT_pLANE_ALPHA)、 具有抖劫 (COPYBIT_DITHER), 然后迸行了 st retch

的调用。
initTempBuffi叫)函數用于初始化巨域的寛、 高等信息, 如下所示:
canst ISurface::BufferHeap& buffers(mBufferHeap);
uint32_t w = mLayer.mTransformedBounds.width();
uint32_t h = mLayer.mTransformedBounds.height();

以上代碼实現的功能是 一介从榆入到榆出囹形的拉伸処理。

12.3 位坎夏制BSP的結枸
copybit 本身用于囹形方面的加速, 也是可造的模玦。 因此实現 copybit 部分, 要使用
帯有加速功能的硬件实現, 使用欬件实現没有意又。
copybit 的实瑰由驱劫程序和硬件抽象昃两介部分組成: copybit 的驱劫程序用于实瑰由
加速功能的硬件到欬件尼的接口; copybit 的硬件抽象尼需要实珗其接口中指定的几介函敖。

�12.3.1 驱劫程序
copybit 的驱劫程序通常是可以迸行囹形方面加速功能的部分。 迏秤驱劫程序通常也基
于硬件实珗, 但在 Linux 中没有杯准的驱劫。 通常情況下, 迏秤驱劫的接口也不是很夏朵,
在简单字符没各或者 MISC 没各中 ioct l 命令就可以实現。

�12.3.2 硬件抽象展的接口
copybit 的硬件抽象扆是 Android 中 一介杯准的硬件模坎, hardware/libhardware/include/

hardware/ 目汞的 copybit.h 文件中定又其接口。


首先定又公共的枚举值, 如下所示:
enum {
COPYBIT MINIFICATION

LIMIT
,. 一
= 1, II 硬件支持的最大的縮小倍數

�218
第12章 位玦隻制 ooe
COPYBIT MAGNIFICATION LIMIT = 2, II硬件支持的最大的放大倍敷
COPYBIT SCALING FRAC BITS = 3, // 縮放引擎支持的粒度
COPYBIT ROTATION STEP DEG = 4, II 支持旋若的角度

copybit_image_t 結杓体表示一 幅囷像,是 copybit 模玦接口中主要使用的數据結杓,其


內容如下所示:
struct copybit image t
uint32 t w; II 寃
uint32 t h; // 高
int32 t format; II 格式, 使用COPYBIT FORMAT xxx
void *base; II image緩沖巨指針
native handle t* handle; II image的句柄
);

copybit_region_t 結杓体表示 copybit 的巨域, 其內容如下所示:


struct copybit_region_t {
int (*next) (struct copybit_region_t const *region, struct copybit_rect_t *rect);
};

copybit_region_t 結杓体中的 next 指針用于找到下一 介匡域, 參數中的 copybit_rect_t


为指出的真正巨域。
copybit 模坎 copybit_module_t 实际上就是杯准的硬件模玦 copybit_module_t, 其中没有
护展其他內容。
copybit 没各的定乂为 copybit_device_t, 如下所示:
struct copybit_device_t {
struct hw device t common;
II 用于没咒參敷
int (*set parameter) (struct copybit_device_t *dev, int name, int value);
II 用于荻得參敖, 返回value
int (*get) (struct copybit_device_t *dev, int name);
II 用于位玦笈制, 从src到dst, region表示其中的巨域
int (*blit) (struct copybit_device_t *dev,
struct copybit_image_t const *dst,
struct copybit_image_t const *src,
struct copybit_region_t const *region);
II 用于拉伸, 从src的src_rect到dst的dst_rect, region表示其中的巨域
int (*stretch) (struct copybit_device_t *dev,
struct copybit_image_t const *dst,
struct copybit_image_t const *src,
struct copybit_rect_t const *dst_rect,
struct copybit_rect_t const *src_rect,
struct copybit_region_t const *region);
};

copybit 主要实現的功能是 blit (位坎夏制)和 stretch (拉伸), 參數为囷像巨域。


set_parameter和 get 接口用于參敖的方式配置迏介模玦。
和 copybit_closeO是模玦的打升和失明函敖, 打玕迥程从 copybit 模坎中
copybit_openO
得到 copybit 没各。

219�
•oo Android 板级支持与硬件相美子系统

�12.3.3 实瑰硬件抽象展
copybit 模玦的接口是非常宏视的定又, 在操作方面, 呈然只有 blit 和 stretch 送两介接
口, 但是通迥它亻I']的參數突型不同及 set_parameter 所没置內容的不同, 可以实現很多秤排
列組合。
实現位玦夏制的硬件抽象扆就是要基于具有加速的硬件驱劫程序,实現 copybit 模玦中
定叉的功能接口。 根据硬件系统的情況, 某些接口可能元法实瑰。 在某些情況下, copybit
``
模玦是可以 自己实瑰, 自己调用,, 的, 因此某些接口也可以使用自定叉的方式, 有造捅
地实現。

3提示: copybit 模決本身可以不实現, 但是 Android 系统框架中使用了 copybit 模決。


因此 copybit 模決一旦实現, 必痰漓足调用者的需求。

12.4 位坎反制的实現

MSM平台中的实瑰
在 MSM 系统中, 具有対 copybit 功能的硬件支持。 从驱劫程序的角度, 迏是通迥
Framebuffer 驱劫程序中的 一 令特定的 ioctl 荻得支持的。
此 ioctl 命令在 MSM 內核代碼的失文件 include/linux/msm_mdp.h 中定又, 如下所示:
#define MSMFB IOCTL MAGIC 'm'
#define_MSMFB
, BLIT 一· IOW(MSMFB IO<;:TL
一 一 MAGIC, 2, unsigned int)

在 MSM 的 Framebuffer 驱劫程序中, 迏介 MSMFB_BLIT 命令是通這调用 MSM 灶理


器的 mdp (Display Processor) 部分代碼实現的: msmfb_blit 是 ioctl 命令的实瑰, 它又调用
了 mdp_device 的 bilt 函數指針的实珗。
MSM 系统 copybit 硬件抽象尼的部分在以下路往中: hardware/msm7k/libcopybit。
其中只有 copybit.cpp -今源代碼文件, 如果是 MSM7k 系列灶理器, 則生成名秣为
copybit.rnsm7k.so 的幼态庠;如果是 QSD8k 系列灶理器, 則生成名杯为 copybit.qsd8k.so 的
幼态庠。 均放置在目才示文件系统的 system/lib/hw 路往中。
copybit.cpp 中模玦打升的实現如下所示:
static int open_copybit(const struct hw_module_t* module, canst char* name,
struct hw device t** device)

copybit_context_t *ctx;
ctx = (copybit_context_t *)malloc(sizeof(copybit_context_t));
memset(ctx, 0, sizeof(*ctx));
II省略部分內容
ctx->device.set_parameter = set_parameter_copybit;
ctx->device.get = get;
ctx->device.blit = blit_copybit;
ctx->device.stretch = stretch_copybit;

�220
第12章位玦隻制 ooe

II省略部分內容
ctx->mFD = open{"ldevlgraphicslfbO", O_RDWR, O); II打玕Framebuffer驱劫程序
//省略部分內容

set_parameter 、 get 、 blit_copybit 和 stretch_copybit 为 copybit 功能的几令实現函數。 之


后升打了 MSM 的 Framebuffer 驱劫程序的没各节燕 /dev/graphics/fbO, 保存到上下文中。
在实現中, stretch_copybit() 函數是主要的实現函數, stretch_copyb頃)函致相蚩于使用
了 stretch_copyb瑱)的部分功能, 也就是困形巨域为整介囹形的情況。
stretch_copybit() 函數实珗的主要片段如下所示:
static int stretch_copybit( struct copybit_device_t *dev,
struct copybit_image_t const *dst, struct copyb訌: image_t const *src,
_
struct copybit_rect_t const *dst_rect, struct copybit_rect_t const *src_rect,
struct copybit_region_:_t const *region) {
struct copybit_context_t* ctx = (struct copybit_context_t*)dev;
int status = 0;
if (ctx) {
struct { II硬件blit功能的消求鮭表
uint32_t count;
struct mdp_blit_req req[l2];
) list;
II省略部分內容: 支持的格式、 巨域大小合理性、 最大尺寸限制等
const uint32_t maxCount = sizeof(list.req)lsizeof(list.req[O]);
const struct copybit_rect_t bounds = { 0, 0, dst->w, dst->h };
struct copybit_rect_t clip;
list.count = 0;
status = 0;
while ((status == 0) && region->next(region, &clip)) (II循坏执行消求
intersect(&clip, &bounds, &clip);
mdp_blit_req* req = &訌st.req[list.count]; II copybit的消求鏟表
set_infos(ctx, req);
set_image(&req->dst, dst);
set_image(&req->src, src);
set_rects(ctx, req, dst_rect, src_rect, &clip);
if (req->src_rect.w< =O I I req->src_rect.h< =O) continue;
if (req->dst_rect.w< =O I I req->dst_rect.h< =O) continue;
if (++list.count == maxCount) {
status = msm_copybit(ctx, &list); II调用实际的执行功能
list.count = 0;

if ((status == 0) && list.count) {


status = msm_copybit(ctx, &list); II调用实际的执行功能

} else { status = -EINVAL; }


return status;

在 stretch_copybit() 函數中, 首先迸行參數检查, 対于不支持的颜色格式和巨域大小,


立刻返回錯课, 不再迸行灶理。 迏是一 介比絞合理的還緝, copybit 模坎的调用者可以根据
返回的措课值, 使用后各方法。
msm _copybit。函數是 MSM 执行 copybit 的功能核心, 奐靑调用驱劫执行相皮的功能,
它的执行如下所示:

221�
•oo Android 板级支持与硬件相美子系统

static int msm_copybit(struct copybit_context_t *dev, void const *list)



int err = ioctl(dev->mFD, MSMFB_BLIT,
(struct mdp_blit_req_list const*)list);
JI省略部分內容

msm _copybit() 调用了 MSM 的 Framebuffer 驱劫程序的 MSMFB_BLIT实瑰 copy 的部


分功能。
MSMFB_BLIT迏仞octl 命令定叉为,以 int 为榆入參啟实际使用的是 mdp_blit_req_
list 奕型的指針, 敷据結杓癸型在 MSM 內核代碼的 include/linux/msm_mdp.h 文件中定又,
而且和 stretch_copyb頃)函敖中定叉的 list 結杓是相同的, 因此參數可以按照上述方式亻考入。

�222
第13章
元綫局域冏系统

13.1 元级局域冏系统概述
W的 (Wireless Fidelity) 使用了 IEEE 的 802.11 仂讠叉的技木, 目前在智能手机中使用

WiFi 已經成为智能手机的核心功能之 。 元紱局域冏的底尼硬件通常是 WiFi 芯片, 迏介
芯片通常提供集成化的 WiFi 功能。
W的系统対上尼的接口包括敷据部分和控制部分, 敷据部分就是 一介通常的两絡没各
(与以太冏非常奕似),控制部分主要用于接入燕操作和安全验证等。在 Android 系统中 W 面
特定实現的內容主要是控制部分。
在欬件尼面上, Android 的 W的系统包括 Linux 內核中的驱劫程序和仂汶、本地部分,
Java 框架癸。 WiFi 向 Java 座用程序尼主要提供控制炎的接口。
Android 元紱局域阿的相栄內容如表 13-1 所示。

表13-1 元鈛局域國系统的相夫內容
Android届次 元鈛局域國系统部分 描 述

硬件昆次 WiFi芯片 可能与盔牙等功能灶于同一介芯片

操作系统扆 wireless的仂汶和驱幼程序 仂汶为通用部分

本地昆 wpa_supplicant、 WiFi适配庠 wpa_supphcant为秧立迸程

Java框架扆 WifiNative 、 WifiService等 包括框架炎和系统服各器部分

Java扆的AP! anciro,d.net w1fi包中的部分炎

13.2 元级局域冏子系统的結枸

�13.2.1 恙体的結杓
Android 元紱局域阿系统自下而上包含驱劫程序和仂汶、 wpa_supplicant 守护迸程、 W的
的 Android 适配庠、 Java 框架中的 WiFi 炎等几介部分, 其結杓如囷 13-1 所示。
• O O Android 板级支持与硬件相美子系统

J,:::三用
亡弓二 :zy-- ;=:;:—一__`

__

android.net.wiFi包

Java框架毘

严. 二二二
Linux內核空何
W面仂讠又

,問 l ii;_;
汕 劻

困 13-1 元紱局域冏部分的結杓

自下而上, Android 元紱局域阿主要包含以下部分。


(1) Linux 內核中 WiFi 仂汶和 WiFi 驱幼程序
WiFi 仂讠又是柝准的內容, 覇Fi 驱劫程序根据具体硬件芯片实現。
C 2) wpa_supplicant
e extemal/wpa_supplicant<N>/: wpa_supplicant 和相美的鋪助模玦的实瑰。

提示: Android使用了不同版本的wpa_supplicant, 如5 、 6 、 8 。

wpa_supplicant 是一 令 Linux 中杯准的 WiFi 管理工具, 在迏里将主要生成可执行程序


wpa_supplicant, 生成庠 libwpaclient.so, 生成用于调试的工具 wpa_cli。
(3) WiFi 本地适配庠
口有wifi.c 源文件。
e hardware/libhardware_legacy/wifi/: /,
e hardware/libhardware legacy/include/wifi/wifi.h: 対外的失文件。
生 成的 內 容是 libhardware_legacy.so 的 一 部 分, 通迥使 用 libwpaclient.so 成为
wpa_supplicant 可执行程序在 Android 系统中的客户端。
(4) Wi历的JNI 部分
e frameworks/base/core/jni/android_net_wifi_WiFi.cpp。
W 即本地対 Java 的支持, 主要提供了対 android.net.wifi 包中 WiFi�的支持。
(5) WiFi的Java 框架部分
e frameworks/base/wifi/java/android/net/wifi/: 实瑰 android.net.wifi 包中的各介突。
e framexworks/base/services/java/com/android/server/: 实珗 WiFi 的服各。
其中, android.net.will 包将作为 Android 的平台 API 提供給 Java 皮用程序扆使用。 在

, 224
第13章元紱局域囹系统 ooe

Settings 皮用音中,调用此控制接口实瑰 WiFi 管理。

�13.2.2 wpa_supplicant 工程
wpa_supplicant 是 WPA 的皮用扆从证客户端,奐賣完成从证相美的登呆、加密等工作。
wpa_supplicant 是 一 介升源瑣目。在 Android 系统中, wpa_supplicant 、 wpa_supplicant_6 、
wpa_supplicant_8 都是其实現 ,分別表示 5 、 6 和 8 三令版本,通 常使 用編洋変量

WPA_SUPPLICANT_VERSION 米控制使用哪 令。

1. 主体可执行程序 wpa_supplicant

編诵的主要結果是可执行程序 wpa_supplicant, 它通常作为一令狓立运行的守护迸程,


其核心是 一 介消息循坏,在消息循坏中灶理 WPA 狀态机、控制命令、驱劫事件和配置信息
等。 wpa_supplicant 有很多控制接口,与上尼的接口 wpa_supplicant 的通信通迥文件形式的
Socket C Unix 套接字),提供命令行 和囹形界面的控制方式 。
wpa_supplicant 的結杓如囷13-2所示。

日 IGUI frontend I

fronteilq control intJfrface

wpa supplicant
、丶 ,
I

EAPOLand

EAP methods
I EAP-TLS IIEAP-MD5 I
I EAP-PEAPII EAP-TILsl

IEAP-GTC II EAP-OTP I
I EAP-SIM IIEAP-AKA I

I EAP-PSK II LEAP I

IEAP趴X IIEAP-FAST I
IEAP-MSCHAPv21

-- --
Ikernel network devi;e driver I

囹 13-2 wpa_supplicant的結杓

在不同的系统中, wpa_supplicant 的主要差昇体現在配置和 驱劫。配置使用 一令外部的


文本文件完成,驱劫 (driver) 則表示 WPA 中的功能模玦,例如,通常使用 wext(Linux wireless
extensions, Linux 元紱护展) 作为 WiFi 的杯准实現,hostap 作为主机实瑰 AP(Access Point),
迏些內容可以在編诵 wpa_supplicant 时迸行配置。

-提示: wpa_supplicant 的各今驱劫(如 vers) 是其功能插件,基本上是硬件元美的內


容,并非 Linux 內核空阅中各今波各的驱劫程序。

225�
•O o Android 板级支持与硬件相美子系统

wpa_supplicant 本身就是 Linux 中的可执行程序, 命令行运行方法如下所示:


41 wpa_supplicant -Dwext -iwlanO -c/etc/wpa_supplicant.conf

以上內容表示使用 wext 作为驱劫, 使用 wlanO 作为接口 (interface), 使用/etc/目呆中


的 wpa_supplicant.conf 作为配置文件。

2. 客户端的庠 libwpa_client.so
編洋生成的幼态庠 libwpa_client.so, 通迥 Socket 和 wpa_supplicant 守护迸程迸行通信,
迏令庠也釆自升源工程, 为了适配 Android 有所更改, Socket 的目 呆放置到了
/data/misc/w面sockets 目汞中, Socket 文件的名杯以 wpa_ctrl_为前綴。 各介 wpa_supplicant
的客户端只需要调用 libwpa_client.so, 并以 wpa_ctrl.h 为失文件, 如下所示:
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); //打升控制端口
void wpa_ctrl_close(struct wpa_ctrl *ctrl); //哭部控制端口
int wpa_ctrl_request(struct wpa_ctrl *ctrl, //友送命令
const char *cmd, size_t cmd_len,char *reply,
size_t *reply一len,void (*msg_cb)(char *msg, size t len));
int wpa_ctrl_attach (struct wpa_ctrl *ctrl); //连接上
int wpa_ctrl_detach(struct wpa_ctrl *ctrl); //接蝕连接
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, s工ze_t *reply len); //接收
int wpa_ctrl_pending(struct wpa_ctrl *ctrl); II检査挂起消息
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); //接蝕连接

wpa_supplicant 与外部的通信都基于以上 一 些函敖完成, 包括控制命令和結果的接收。



wpa_ctrl 結杓則是 令內部結杓, 调用者不需要掌握其具体的結杓, 在打玕后作为句柄返
回, 后面的操作以此句柄作为參數。 具体的信息均以字符串米表示, WPA_CTRL_玕失的
宏表示控制命令, WPA_EVENT_升失的宏喃皮事件。

3. 命令行程序 wpa_cli

編涌結果 wpa_cli 是 wpa_supplicant 的命令行客户端 (command-line client), 它镱接了


libwpa_client.so 劫态庠。
wpa_cli 使用方式如下所示:
# wpa_cli 一 -help
unknown option -- -wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvBJ [-a<action
file>] [-P<pid file>]�[-g<global ctrl>] [command .. ]

wpa_cli 主要使用的參數如下所示。
a quit: 退出。
a add_network: 垓命令佘返回新增加的阿絡 ID, 一
般是 O。 下面命令的第 一介參數就
是冏絡的 ID 。
a set_network: 没置两絡參敖。
a scan: 扭描冏絡清求。
a select_network <networ知d>: 造掙冏絡(禁止其他的)。
a enable_network <network id>: 使能冏絡。
a disable network <network id>: 禁止冏絡。

�226
第13章 元紱局域阿系统 ooe

e interface [ifname] : 没置和造掙接口。


使用 wpa_cli 的 一 些命令示例如下所示:
It wpa_cli -iwlanO add_network 菲 增加两絡o
# wpa_cli -iwlanO set_network O ssid'"test"' # 没置冏絡0的SSID
It wpa_cli -iwlanO set_network O key_mgmt WPA-PSK # 没蹬冏絡管理方式为WPA-PSK
It wpa_cli -iwlanO set_network O psk '"12345678"'
非 wpa_cli -iwlanO set_network O pairwise TKIP
# wpa_cli -iwlanO set_network O group TKIP
It wpa_cli -iwlanO set_network O proto WPA
It wpa_cli -iwlanO enable_network 0 # 使能冏絡0

使用 interface 命令可以查看昚前使用的接口, 如下所示:


ii wpa_cli·interface
Using interface 'ethO'

使用 list_network 命令可以査看圭前具有的冏絡, 如下所示:


j/ wpa_cli 訌 st_network
Using interface 'ethO'
network id/ ssid/ bssid/ flags
##省略各介元紱AP的信息

�13.2.3 WiFi本地适配庠
在 WiFi 的使用這程中, 査找 AP 和连接 AP 是元紱冏絡的重熹內容, 在完成连接后,
使用的迥程癸似于有紱阿絡。
hardware_legacy 中的 wifi.c, 是 Android 中的 WiFi 适配扆。 从欬件扆次矢系上, 它和
wpa_cli 以相同的方式调用 wpa_supplicant, 均通逍 libwpa_ clientlwpa _ ctrl.h 染文件提供的
接口迸行交互, 并且需要连接庠 libwpaclient.so (wpa_supplicant 的客户端)。
失文件 wifi.h 是 Android 中的 WiFi 适配扆対 JNI 部分的接口。 wifi.h 的接口包括 一 些
加栽和连接的控制接口, 主要是 wifi_command() 和 wifi_wait_for_event() 迏两介接口, 如下
所示:
int wifi_command(const char *command, char *reply, size_t *reply_len);
int wifi wait for event(char *buf, size t len);

wifi_commandO提供将命令友送到 W面系统下展的功能; wifi_wait_for_event() 奐靑事


件的 迸入通道, 迏令函數将被阻塞, 直到收到 一 介 WiFi 事件, 并以字符串的形式返回。
在 wifi.c 的实現中, wifi_commandO 函數调用 wifi_send_command() , 再通迥调用
wpa_ctrl_reque頭)函敖直接向 wpa_supplicant 迸程岌送命令, 并通這事件得到返回內容。
在其实瑰中, wifi_load_dri verO 奐賣加 栽驱 幼 , 默从情況下 WiFi 的 驱劫路往为
"/system/lib/modules/wlan.ko", 迏秤逆緝和 wpa_supplicant 本身元美, 只是加栽內核中以模
玦形式存在的驱幼。

227�
•oo Android 板级支持与硬件相美子系统

�13.2.4 WiFi的JNI部分
android_net_wifi_WiFi.cpp 文件是 W面的 JNI 部分, 它实現了 android.net.wifi 包中的
內部哭 Wifi.Native。
在 JNI 中, 除了加裁驱劫, 坯有很多渚如 xxxxCommand 形式, 调用了 WiFi 本地适配
庠, 大部分调用最終也通這 wpa_supplicant 完成。 各秤函數用于字符串命令的友送, 它亻fJ
实际上都是通迥调用 doCommand()函數实現的。

�13.2.5 WiFi的Java 扆

1. android.net.wifi 包核心部分

在 Javza 框架中 WiFi 部分在 android.net.wifi 包中, 其中几令主要突如下。


• WifiNative: 內部癸, JNI 实瑰的炎, 与下展交互。
• WifiMonitor: 內部癸, 用于監測, 调用了 WifiNative。
• WifiStateTracker: 內部奕, 提供主要還緝, 用于狀态的跟綜, 调用了 WifiNative。
• WifiManager: 作为 API 的主要接口。
WifiManager 用于灶理元紱冏絡相美的管理功能:配置冏絡、灶理已經活劫两絡的信息、
荻得 AP 的扭描信息等。
WifiManager 癸中主要的控制癸方法如下所示:
public boolean setWifiEnabled (boolean enabled) II使能WIFI
public int addNetwork (WifiConfiguration config) II增加冏絡
public int updateNetwork (WifiConfiguration config) II更新冏絡
public boolean removeNetwork (int netid ) II刪除冏絡
public boolean disconnect () //新升连接
public boolean reconnect () //匯新连接
public boolean startScan () //玕始扭描

WifiManager 的主要信息荻取方法如下所示:
public int getWifiState () //荻得狀态
public List<Wi丘Configuration> getConfiguredNetworks () //荻得配咒信息
public List<ScanResult> getScanResults () //荻得扭描結果
public Wifiinfo getConnectioninfo () //荻得连接信息
public Dhcplnfo getDhs:;pinfo ()_ //荻得DHCP信息

getWifiState()的結果包含的 一 些敷值用 WIFI_STATE_ 升失的整數表示 WiFi 的狀态。例


如, 已經禁止, 正在禁止, 已經使能。
getConfiguredNetworks() 用于返回阿絡的配置信息, 結果为 WifiConfiguration 奕型的列
表, 表示各今配置信息。

WifiConfiguration 奕表示 介两絡的配置信息,由 一組公共的厲性組成: networkld (两
絡 id) 、 BSSID C冏絡地址)、 SSID C冏絡名t$)、 priority (优先级) 等。

getScanResults()表示 介两絡的扭描結果, 結果为 Wifilnfo 癸型的列表。

ScanResult 表示阿絡的配置接口, 由 組公共的厲性組成: BSSID (冏絡地址)、 SSID

, 228
第13章元紱局域冏系统 oo•

(阿絡名�)、capabilities (阿絡 能力)、frequency (整型MHz, 通道的颜率) 和level (整型


dBm, 信弓级別)。
getConnectioninfo()的返回奕型为 Wifi血fo, 此突包含 一系列的 get接口來荻得信息。
由于元紱局域阿方面的接口提供的是控制管理方面的,因此在不同的Android没各中,
只要具有Wi氏硬件, 运行的情況就不合相差太多。

2. WifiService部分
WifiService 是运行在 system_server 中的部分,提供 WiFi 系统的核心控制功能。
android.net包中的 IWifiManager.aidl提供了到WifiService的接口,部分內容如下所示:
interface IWifiManager

List<WifiConfiguration> getConfiguredNetworks();
int addOrUpdateNetwork(in WifiConfiguration config);
boolean removeNetwork(int netid);
boolean enableNetwork(int netid, boolean disableOthers);
boolean disableNetwork(int netid);
boolean pingSupplicant();
void startScan(boolean forceActive);
II省略部分內容

WifiService继承了IWifi.Manager.Stub, 提供了使能、禁止阿絡、扭描、得到配置等功
能。android.net包中作为API的 WifiManager的部分方法,也是通這封裝対其 调用完成的。
WifiService中的 一 介芙鍵功能是多播(Multicast)的管理。

3. Settings成用部分
在Settings程序中, 完成了対WiFi系统的调用,主要這程有三令步驟:升启W面、查
找 AP 和连接 AP。 其中,玕启 W昉的這程包括初始化、加裁驱劫 、 运行wpa_supplicant
等步骕,而査找AP和连接AP步骕則是通迥友送命令和等待下扆回皮的方式米完成的。

13.3 元级局域冏BSP的結枸

�13.3.1 t办讠又和驱劫程序

在Linux中, 元綫局域阿包括仂讠又和驱劫程序两部分的內容,対用户空l 司提供的是冏
絡 驱劫程序。
WiFi仂汶的主要接口是include/net/目汞中的 wireless.h。
WiFi仂汶的实瑰源文件 在net/wireless/目汞中, 一 般都要使用wext-core.c、wext-priv.c
和wext-proc.c 等几介源代碼文件。
在內核配置菜单中,WiFi 仂讠叉的內容 在Networking support=> Wireless中造定,其中
Wireless extensions和Wireless extensions sysfs files迭項表示使用ioctl或者sysfs対元紱冏絡
迸行附加的 控制。

229�
• O O Android 板级支持与硬件相美子系统

W 酌的驱劫程序在 drivers/netlwireless/目汞中。 在內核配置菜单中, WiFi 驱劫程序配


置迭項为: Device Drivers=>Network device support=>Wireless LAN。 WiFi 驱劫部分的文件
比絞多,包含多介子目呆是不同的 WiFi 没各的驱劫程序。其中可能包含的內容表示不同的
元限局域冏实珗的驱劫程序, 配置通常坯需要没置固件 (f1rmware) 的路往。 WiFi 驱劫程
序的形式通常是一 令冏絡没各 (net_device)。
在 Android 中, w面驱劫程序 一 般編诵成內核模玦的方式, 通這皮用程序没置玕美迸
行加裁和卸戟。 同时,要使 WiFi 芯片正常工作,驱劫里面通常坯需要实現侥写固件程序和
一些初始化配置到 W面芯片的還緝。

3 提示: W 的秘汊和驱劫的主要去文件是 wireless.h, 迏今*文件中没有定乂函敖,但


是定乂了大量和 WiFi 相美的救据結枸和常量。

�13.3.2 用户空阅的內容
WiFi 在用户空阅的部分是柝准的, 主要功能基于 wpa_supplicant 实現, 与 Android 其
他部分的硬件抽象尼相比, Wi历部分基本上是杯准的。

1. 配置的差畀
不同系统的 WiFi 之岡的差昇主要在于 一 些配置工作, 即 wpa_supplicant.conf 文件。此
文件的示例在 wpa_supplicant 工程中,其中的注梓描述本文件的格式如下所示:
update_config =l
ctrl interface=wlanO
ap_scan =l

wpa_supplicant.conf 文件放置在目杯文件系统中的Isystem/etc/wifi/目汞中,作为固定的
配置文件。 系统运行时, 在 /data/misc/wifi/目汞中将生成 wpa_supplicant.conf 文件。 迏是幼
态生成的元紱冏絡的配置文件, 与原米的 wpa_supplicant.conf 并不完全相同,其中包含各
神阿絡信息, 在 init.rc 中将佘把迏神文件的枚限改为 0660。
一介 wpa_supplicant.conf 文件的格式如下所示:
network= {
ssid= "abcd"
psk= "hello"
priority=ll

此灶的 network={} 內容表示一 介元紱两絡, 根据颎序排列成优先级。

2. 插件庠
wpa_supplicant 根据指定名字镱接靜态庠, 作为 wpa_supplicant 的插件使用。 在編诵
wpa_supplicant 可执行程序的 Android.rnk 文件中, 有下面形式的定又:
include $ (CLEAR VARS)
LOCAL_MODULE : = wpa_supplicant
ifdef CONFIG DRIVER CUSTOM
LOCAL STATIC LIBRARIES : = libCustomWifi

�230
第13章 元鈛局域國系统 ooe

endif
ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)')
LOCAL STATIC LIBRARIES += $(BOARD WPA SUPPLICANT PRIVATE LIB)

此灶使 用 的 靜态庠或者是 固定 名 稔的 libCustomWifi.a , 或者由 板 级 的 編 诵宏



BOARD_WPA _SUP PLICAN T_P RIVATE_LIB米定叉 介靜态庠 名稔 。

在wpa_supplicant_8中 ,增加了HostAP的支持, 因此坯需要使用另外 介板级的編诵
宏BOARD_HOSTAP D_P RIVATE_LIB表示HostAP的插件庠。

13.4 元级局域冏BSP的实現

対于元紱局域阿 部分,由于实現 相対柝准 , 钳対特定的硬件平台需要移植和改劫的內


容不多。
Linux內核方面, WiFi在Linux內核中有杯准的冏絡仂汶, 各介硬件平台相差別的部
分只有W的芯片的驱幼程序 。 迏些 驱劫程序是針対芯片级別的, Android中的实現和其他
Linux系统中的实現基本上是相同的。

Android用户空向使用了杯准的 wp a_supplicant, 迏是 Linux 中的 令柝准 实瑰。 在
Android中,一般不需要为WiFi增加单袖的硬件抽象尼代碼,但是可以迸行部分配置工作。

�13.4.1 基于BCM4329的方案(Nexus One和Nexus S)



Broadcom的BCM4329是WiFi (802.1 l n)、 盔牙(2.1)和FM ( TX和RX) 体的芯
片, Nexus One和Nexus S等系统使用垓方案。

1. Linux 內核中的內容

BCM4329的驱幼程序在 drivers/net/wireless/bcm4329/目呆中, 它依賴于元紱护展仂汶



(wireless extensions), 般生成bcm4329.ko內核模坎。在驱劫的配置方面,主要有两令宏:
BCM4329_FW _PATH表示WiFi 芯片固件的路往, 固件的名稔 一 般为fw_bcm4329.bin; 另

-今宏是 BCM4329_NVRAM_PATH, 表示 NVRAM 在 proc文件系统中的路往, 般为
/proc/calibration。

2. 用户空阅的內容

在Android系统中, 元紱局域阿系统 可以迭捅劫态使用內核中的代碼, 因此BCM4329



芯片驱劫程序以內核模玦的形式存在, 其 路往 般为: system/lib/modules/bcm4329.ko。

Android的元紱局域两系统 坯需要 配置几介宏, 迏些內容 般在 BoardConfig.mk文件
中定乂例如,Nexus One的路往 device/passion-common/BoardConfigCommon.mk, Nexus S
的路往 device/samsung/crespo/BoardConfig.mk。
其中定乂內容的路往为:
BOARD''WPA ' SUPPLICANT ' DRIVER : = WEXT
WPA' SUPPLICANT VERSION := VERO 6' X
BOARD WLAN DEVICE : = bcm4329
WIFI_DRIVE__B_MODULE_PATH _ : = -'�/system/lib/modules/bcm4329. ko"

231�
•oO Android 板级支持与硬件相美子系统

WIFI 一DRIVER一FW一STA.一PATH : = "/vendor/firmware/fw_bcm4329.bin"


WIFI DRIVER FW AP PATH : = "/vendor/firmware/fw_bcm4329_apsta.bin"
WIFI DRIVER MODULE ARG : = "firmware_path=/vendor/firmware/fw_bcm4329.bin
nvram_path=/proc/calibration"
WIFI DF,JVtR MODULE NAME _; �b<::m4329"

BOARD_WPA_SUPPLICANT_DRIVER 定乂的數值为 wpa_supplicant 所使用, 其他內


容(如 WiFi 驱劫模玦的路往等)在 BCM4329 的相美实瑰中使用。
BCM4329 相栄內容的代碼路往为: hardware/broadcom/wlan/bcm4329/, 其中 fmnware
目泵放置的就是 BCM4329 的固件, 它亻[]都将被放置在目柝系统的/systern/vendor/frrmware
目汞中。
config 目呆中則放置了配置文件, 系统的 wpa_supplicant.con[ 文件的內容如下所示:
update_config=l
ctrl interface=ethO
eapol_version= l
ap_scan= l
fast :p尹th=l

另 一 今文件为幼态荻得 IP 的 DHCP 配置文件 android_dhcpcd.conf, 內容如下所示:


interface ethO
option su!Jn至t一項_ask, rou.ters, domain__!1ame_yervers

垓文件将被放置到目才示系统的 system/etc/dhcpcd 目景中。

�13.4.2 OMAP平台的一介典型实瑰

1. Linux 內核中的內容

OMAP3 平台在 Android 2.3 平台的元紱局域冏部分在內核中的实現比絞杯准。 包含


W 的的仂讠文部分和驱劫程序。 在用户空伺, 生成名稔为 tiwlanO 的冏絡没各。
在 Android 玕源工程中, 以下几介目泵为 Wlan 驱劫程序的相美部分:
• system/wlan/ti/wilink_6_1/platforms/。
• system/wlan/ti/wilink_6_1/external_drivers/。
• system/wlan/ti/wilink_6_1/stad/。
在 TI OMAP 的 Zoom 平台上, WiFi 部分通辻 SDIO 惡紱连接到系统上,因此驱劫程序
中包含了 SDIO 恙紱连接的部分和 Wlan 芯片的驱劫部分。

三提示: 送部分驱劫程序考痣了 Linux 中的不同版本甚至跨平台的情況, 因此其中包


含了不同的編诵宏。

system/wlan/ti/wilink_6_1/platforms/os/linux/src/目呆中的 WlanDrvlf.c 文件,是驱劫程序


的入口。 其 中 模 玦 的初始化函啟 wlanDrvlf_Modulelnit() 调用 wlanDrvIf_Create() 。 在
wlanDrvlf_Crea圖)函敖中将注朋 WiFi 的阿絡没各 (net_device 結杓)。
在 Linux 2.6.31 以下版本的 Linux 內核中,将 wlanDrvlf_Ope瑱)賦值給 net_device 的 open
函數指針。 在 wlanDrvlf_Ope填)函數中, 没置冏絡友送敷据的 hard_start_xmit 指針。
在 Linux 2.6.31 及以上版本的 Linux 內核中, 将通這 net_device_ops 哭型的两令結杓体

, 232
第 13 章 元紱局域阿系统 ooe

tiwlan_ops_dummy和tiwlan_ops_pri完成奕似的操作。
此驱劫程序經近編诵连接之后, 将生成名稔为tiwlan_drv.ko的內核模玦。

2. 用户空阅的实视
一 '`
TI的OMAP平台元紱冏絡的仂汶比絞特殊, 主要体現在它使用了 令特殊的 驱劫,,
替代了wpa_supplicant中的wext杯准驱劫实現。
在Android升源代碼中, 迏部分驱劫的代碼路往为: system/wlan/ti/w山呔_6_1/。
其中wpa_supplicant_lib/目呆中的Android.mk文件如下所示:
include $(CLEAR_VARS)
LOCAL MODULE : = libCustomWifi
LOCAL SHARED LIBRARIES : = libc libcutils
LOCAL_CFLAGS : = $(L_CFLAGS)
LOCAL_SRC_FILES : = $(0BJS)
LOCAL_C_INCLUDES : = $(INCLUDES)
include $(BUILD一一STATIC_LIBRARY)

统迥灶理将編洋生成靜态庠libCustomWiFi.a (表示自定叉的Wi历庠)。 迏令庠将被


wpa_ supplicant可执行程序连接, 作为插件使用。
system/wlan/ti/wilink_ 6_ l /config/目杲中的wpa_supplicant.conf为OMAP平台的配置文
件, 內容如下所示:
ililililil wpa_supplicant configuration file template Ui/U
update_config =l
ctrl_interface =tiwlanO
eapol_version =l
ap_scan =l
fast reauth=l

迏里指定的tiwlanO是OMAP平台冏絡没各的名杯。
在system/wlan/ti/wilink_6_ 1/config/wpa_supplicant_lib/目汞中的 driver_ti.h文件, 具有
如下的定乂:

``
迏里的" tiwlanO "是 wpa_supplicant 中的一 介 驱劫"的名稔, 从遷緝上, 它与wext、
hostap等驱劫是并列的美系。
在同目呆 driver_ti.c中,定叉了wpa_driver_ops 奕型的結杓体wpa_driver_custom _opsm,
表示対WPA"驱劫"的操作:
canst struct wpa_driver_ops wpa_driver_custom_ops = {
.name = TIWLAN ORV NAME, II驱劫的名杯
.desc = "TI Station Driver (1271)",
.get_bss這= wpa_driver_tista_get_bssid,
.get_ss這= wpa_driver_tista_get_ss這,
.set_wpa = wpa_driver一户sta_set_wpa,
.set_key = wpa_driver_tista_set_key,
.set_countermeasures = wpa_driver_tista_set_countermeasures,
.set_drop_unencrypted = wpa_driver_豆sta_set_drop_unencrypted,
.scan = wpa driver_tista_scan,
II省略部分內容
);

233�
• O o Android 板级支持与硬件相美子系统

在 external/wpa_supplicant 的 driver.c 中, 将自劫取名为 wpa一如ver�custom_ops 的結杓


`` ``
体, 注冊为自己的一 介 驱劫 "0 迏里的內容将实現名稔为"tiwlanO" 的 驱劫"' 迏介驱劫
实际上是対 wext 內容的护展。因此,在 OMAP 平台的元紱局域两的实現中, wpa_supplicant
呈然使用的"驱劫"名稔是"tiwlanO", 但是原本 wext 部分也是需要被使能的。

�13.4.3 Galaxy Nexus的实瑰


Galaxy Nexus 系统使用 BCM4330 模玦实珗元鈛局域阿,基于 SDIO 端口连接,欬硬件
結杓继承自 Tuna 板。

1. Linux 內核中的內容

Nexus Galaxy 的 BCM4330 驱劫程序在 drivers/net/wireless/bcmdhd/ 目汞中, 迏是


Broadcom 4329/30 芯片的內核实瑊也使用了 net/wireless/ 岂中的仂汶部分。
由于需要対不同的平台和硬件的细微差別做出支持,因此本部分的源代碼文件比絞多,
結杓也比絞夏朵, 有些代碼甚至不是 Linux 系统考用的。 由于使用 SDIO 连接, 因此送部
分代碼也需要 MMC 部分的支持。 除了基本的元綫局域阿的支持之外, 述支持 P2P 的燕対
熹等连接方式。
wl_android.c 是垓部 分 內 容在 Android 系 统 中 的 考汀实 現 。 用 于初 始 化 的
wl_android_init()函數如下所示:
int wl android init(void) {
int ret = 0;
dhd' msg 'level i= DHD ERROR VAL;
hfdef ENABLE INSMOD NO FW LOAD
dhd download fw on driverload = FALSE;
fondif /* ENABLE 'INSMOD NO FW"LOAD */
hfdef CUSTOMER HW2
if (!iface_name[O)) {
memset(iface_name, 0, IFNAMSIZ);
bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ);

41endif /* CUSTOMER HW2 */
return ret;

其中所使用的 CUSTOMER_HW2 等宏, 用于巨別不同板的实現。


在初始化之后调用的 wl_android_post_in頃)函數如下所示:
void wl_android_post_init(;void) {
if (!dhd download fw on driverload) (
dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); //通這GPIO的控制
g_wifi_on = O;

迏两令部分被 dhd_linux.c 文件中的初始化函敖 dhd_module_in填)先后调用, 完成在


Android 系统中的特定初始化。

'二
用于打升 WiFi 的 wl_android_wifi_on()函數如下所示:
第 13 章 元紱局域同系统 ooe
int ret = 0;
if (!dev) { return -EINVAL; }
dhd net if lock(dev);
if (!g_wifi_on) {
dhd ' customer_gpio' wlan' ctrl(WLAN 'RESET ON); // GPIO的控制
sdioh_start(NULL, OJ;
ret = dhd_dev_reset(dev, FALSE); · //没各及位
sdioh_start(NULL, l);
if (!ret) {江(dhd_dev_init_ioctl(dev) < 0) ret = -EFAULT;} //硬件控制
g_wifi_on = l;

dhd' net 'if unlock(dev);


return ret;

呈然 BCM4330 核心的实現部分相同, 但是不同的平台中玕美 W 酌需要使用特殊的

GPIO 引胸迸行控制, 因此玕美迥程中需要有特殊的硬件控制。


驱劫程序实現后,将対用户空 f司提供wlanO 和 p2p0 两令阿絡没各,前者是元綫局域冏
的基本没各, 后者是燕対燕的连接没各。
Tuna 的板级支持文件 arch/arm/mach-omap2/board-tuna-wifi.c 中定又了 WiFi 玕芙的操
作 。 其 中 定 又了 wifi_platform_data 癸型的 tuna_wifi_control , platform_device 癸型的
tuna_ wifi _device, 其名林为"bcmdhd_wlan", 包含 tuna_wifi_control 为其中的數据。
通迥 GPIO 控制的 WiFi 初始化迥程如下所示:
static void _init tuna_wlan_gpio(void) {
omap mux init signal("sdmmcS cmd", OMAP PIN INPUT PULLUP); II MMCS命令
omap mux init signal("sdmmcS elk", OMAP PIN INPUT PULLUP); II MMCS的时鉀
omap mux init signal("sdmmcS datO", OMAP PIN INPUT PULLUP); II MMCS的致据
omap mux init signal("sdmmcS datl", OMAP PIN INPUT PULLUP);
omap mux init signal("sdmmcS dat2", OMAP PIN INPUT PULLUP);
omap mux init signal("sdmmcS dat3", OMAP PIN INPUT PULLUP);
omap_mux_init_signal("sim_reset.gpio_wk2", OMAP_PIN_INPUT); II BCM4330的GPIO
omap mux init signal("kpd rowl.safe mode", 0);
omap_mux_init_signal("gpmc_ncs7.gpio_l04", OMAP PIN OUTPUT); II GPIO的GPIO


omap4 ctrl wk pad writel(OxbOOOOOOO,
MAP4 CTRL MODULE PAD WKUP CONTROL USIMIO);
gpio_request(GPIO_WLAN_IRQ, "wlan_irq");
Ill通這GPIO使能WiFi的屯源

II使能GPIO
gpio_direction_input(GPIO_WLAN_IRQ);

WiFi 方面的控制通常是升美和屯源方面, Tuna 板的 WiFi 也是通迥 SDIO 连接, 并通


迥 GPIO 控制升美和屯源部分, 因此具有代碼中的操作。

2. 用户空岡的的內容
由于继承自 Tuna 板的实現, devices/samsung/tuna 目呆者中的 BoardConfig.mk 文件定
又了 W面相美的內容, 如下所示:
BOARD WPA SUPPLICANT DRIVER := NL80211
WPA SUPPLICANT VERSION : = VER O 8 X
BOARD WPA SUPPLICANT PRIVATE LIB : = 訌b driver cmd bcmdhd
BOARD HOSTAPD DRIVER : = NL80211
BOARD HOSTAPD PRIVATE LIB : = lib driver cmd bcmdhd
BOARD WLAN DEVICE : = bcmdhd

235�
• O O Android 板级支持与硬件相美子系统

WIFI DRIVER FW PATH PARA M := "/sys/module/bcmdhd/ parameters/firmware _path"


#WIFI_DRIVER_MODULE_PATH := "/system/lib/modules/bcmdhd .ko"
WIFI DRIVER FW PATH STA := "/vendor/firmware/fw bcmdhd.bin"
WIFI_DRIVER_FW_PATH_P2P := "/vendor/firmware/fw_bcmdhd_p2 p .bin"
WIFI_ Df.I�圭立-胚'I'H」>_P �:= "/v�.ng.qr,Lt,,;i,,:i;-m四;ffw_bc邢坦生ae§;_ta .bin"

目才示系统的 /vendor/伍mware/ 目汞中具有 bcm4330.hcd、 fw_bcmdhd.bin、 fw_bcmdhd


apsta.bin和fw_bcmdhd_p2p.bin等二迸制的文件。由于驱劫程序使用了編入內核的方式,因
此不需要使用硬件模坎的ko 文件。
lib_如ver_cmd_bcmdhd.a就是Nexus Galaxy所使用的wpa_supplicant_8的插件庠, 同
时被wpa_supplicant和hostapd两令可执行程序所镱接, 作为二者的插件庠使用。
插件的代碼路往为: hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib/, 将生成
lib_driver_cmd_bcmdhd.a靜态庠。 driver_cmd_wext.c 将作为元紱局域冏基本部分的插件使
用, driver_cmd_nl8021 l .c将作为HostAP部分的插件使用。

�236
第14章
盔牙系统

14.1 盔牙系统概述

在Android中, 藍牙系统的底扆硬件是藍牙硬件, 通常可以使用UART、SDIO或USB


接口作为连接。
盔牙系统対上屄的接口通這比絞通用的接口伟逸給Java展, 在Android皮用程序扆通
迥调用藍牙系统的接口实現盔牙的各秤規范。
Android盔牙系统的核心围统BlueZ实瑰,它是Linux平台上一 套完整的盔牙仂汶枝玕
源实珗, 目前被「泛皮用在各秤Linux友行版中, 并被芯片公司移植到各移劫芯片平台。
BlueZ的仂汶找在Linux內核中已經包含;而BlueZ的用户空冏实瑰, Android根据通用的
部分在系统中做出集成, 并在Java框架尼提供服各。
Android藍牙的相栄內容如表14-1所示。

表14-1 藍牙系统的相夫內容
Android展次 盔牙系统部分 描 述

硬件扆次 藍牙芯片 通近串口连接, 芯片常与W的及用

操作系统昆 bluetooth的仂汶和驱劫程序 仂汶为通用部分

本地展 BlueZ用户空冏、 Android的藍牙庠 bluetoothd为秧立迸程

Java框架扆 android.server、 android.bluetooth包 有部分运行于系统服各器迸程

Java扆的AP! andro1d.bluetootb包的几介突 包括Socket和通信部分

14.2 盔牙子系统的結枸

�14.2.1 盔牙系统的結枸
Android盔牙系统自下而上包含驱幼程序和仂汶、BlueZ庠、BlueTooth的Android适配
庠、 Java框架中的BlueTooth癸等几令部分, 其結杓如囹14-1所示。
• O O Android板级支持与硬件相美子系统

盔牙寂HJ BlueTooth包
Java皮用昆

Java框架毘

C框架居

Linux內核扆
盔牙l;l;i,Sl扆

移植
部分 1 盔牙的rfkill 盔牙驱劫(UART, USB..)

囹 14-1 Android藍牙系统的結杓

藍牙系统分 为以下几介居次 。
(1) 內核居
藍牙系统的內核部分有两令尼次, 分別是盔牙的驱劫程序和藍牙的仂汶扆。
(2) BlueZ庠
• external/bluetooth/: BlueZ在用户空向的庠。
生成libbluetooth.so、bluetoothd以及bcidump等焱多相美工具和庠。BlueZ提供用户空

向 藍牙方面的支持, 包含 介主机控制仂汶CHCI), 以及其他焱多內核实現仂汶的接口。
同时, 实現了藍牙的所有皮用模式(Profile)。
(3) BlueTooth的适配庠
• system/bluetooth/: Android的BlueTooth 适配庠。
生成libbluedroid.so, 直接与rfkill和藍牙的Socket交互。
(4) BlueTooth的JNI部分
• frameworks/base/core/jni/: android_bluetooth_*和android_server_Bluetooth *等文件。
提供android.bluetooth包中的几介癸和android.server包中和Bluetooth 相美的几介奕的
支持。 生成的內容是Android的JNI 庠 libandroid_runtime.so的一 令部分。
(5) Java框架扆
• frameworks/base/core/j avalandroid/ server: 盔牙部分的服各代碼路往。
• frameworks/base/core/java/android/bluetooth: 盔牙包(包括対皮用的APO。
Bluetooth 的 服各部 分奐靑管理并使 用底尼的本地服各, 并封裝成系 统 服各 。
android.Bluetooth包中的各介奕是藍牙的平台API部分, 提供給皮用程序扆使用 。
(6)服用尼
盔牙的相栄没置部分 在Android的Setting包中, 路往为: packages/apps/Settings。

, 238
第14章 盔牙系统 ooe

盔牙皮用部分opp(文件亻賴榆)和pbap(屯话本访阅)通這使用obex庠和藍牙RFCOMM
礽以实現。 迏部分代碼在Bluetooth 包中, 路往为: packages/apps/Bluetooth。

�14.2.2 BlueZ

1. BlueZ架杓
盔牙系统的核心是BiueZ, 因此JNI和上尼都围统与BlueZ的洵通迸行。 藍牙适配展
(libbiuedroid.so)在負責用户空岡的适配工作。
Android盔牙系统的結杓如囷14-2所示。

框架扆

內核
HCI SOP RFCOMM
BlueZ
L2CAP 內核空阿

困14-2 Android 藍牙系统的细节結杓

盔牙部分各牙軒办汶之阅的失系如囹14-2所示。
BlueZ生成的主要組件包括若干令劫态庠和可执行程序。
(1)劫态庠
• libbluetooth: BlueZ公共庠, 被各內部皮用使用, 包含如SDP、 HCI等仂以找內部
接口。
• libbluetoothd: 主机接口的实現, 作为守护迸程运行。
• audio: 音颜服各的插件。
• input: 榆入服各的插件。
• liba2dp: 盔牙立体声服各。
(2)可执行程序
• bluetooth: 主机接口的实現 , 同时也是核心守护迸程, 提供対外的D-BUS接口。
• hciconfig、 hcitool、 hciattach: 主机接口輔助工具。
• sdptool: 提供SDP服各相美管理接口。
• rfcomm: RFCOMM配置工具。

239�
•O o Android板级支持与硬件相美子系统

2. 藍牙的各介可执行程序
藍牙芯片的初始化部分流程主要通迥BlueZ的工具 hciattach 迸行。 垓工具实瑰在
Android的以下路往中 : external/bluetooth/tools/。
hciattach 中定叉了很多芯片的特定初始化流程(基于UART), 以及使用的初始化仂汶
(如BCSP 、 H4 、 LL等)。很多芯片都有其特定的初始化流程, 比如需要下裁固件等, 因此,
可 以根据芯片奐型迸行造掙。一般米讽, 很少佘遇到 hciattach中不支持的芯片。如果遇到,

需要対hciattach迸行护充, 或者在调用 hciattach前, 提前迸行 些初始化 工作。
hciattach的命令格式为:
hciattach [-n] [-pl [-bl [-t timeout] [-s initial_speed] <tty> <type I id> [speed]
[flow I no flow) [bdaddr]

其中的重要域如下所示。
• tty: 指定綁定的tty。
• typelid: 指定需要 初始化的芯片癸型, 或者Vendor/Product ID。
•. speed: UART速率。
• bdaddr: 藍牙MAC地址, 可 造。

另 令重要的配置工具为hciconfig, 垓工具和面onfig哭似。 一旦hciattach完成, 即
可通迥它将其激活:
hciconfig hciO up

tools目呆坯包含hcitool 、 bccm d等輔助工具。 除此之外, BlueZ坯提供hcidump, 可


以查看HCI通信的细节。 在此不再洋述。

3. Android中的藍牙服勞

在藍牙服各方面, 般使用 初始化朐本init.rc中的默从部分即可, 如下所示:
service bluetoothd /system/bin/bruetoothd -n
socket bluetooth stream 660 bluetooth bluetooth
socket dbus bluetooth stream 660 bluetooth bluetooth
group bluetooth net_bt_admin misc
disabled
service hfag /system/bin/sdptool add --channel=lO HFAG
user bluetooth
group bluetooth net bt admin
disabled
oneshot
service hsag /system/bin/sdptool add --channel=11 HSAG
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot
service opush /system/bin/sdptool add --channel=12 OPUSH
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot
service :p2ap /system{bin/�dptool add --channel=l 9 PBAP

�240
第 14 章 藍牙系统 ooe
user bluetooth
group bluetooth net_bt_admin
disabled
oneshot

以上几秤Android服各 hfag、hsag、opush和pbap均使用了 oneshot 方式, 表示只运行


一次。 而bluetoothd 是 BlueZ的守护迸程, 没有使用 oneshot, 启劫后坯需要通迥 sdptool
启劫相皮的BlueZ服各。

�14.2.3 bluedroid庠
libbluedroid.so是Android系统中的藍牙适配庠,主要实瑰的功能是対盔牙没各的管理,
比如屯源管理操作 等 。 盔牙没各的屯源管理 通這內核的rfkill机制实現 。
bluetooth.h中的內容提供了対上尼文件操作的函數, 如下所示:
int bt_enable (); II使能藍牙
int bt_disable (); II禁止盔牙
int bt is enabled(); II査看藍牙使能和禁止狀态

在bluetooth.c实現的內容中, 主要通迥调用 hci的失文件与BlueZ的用户空阅部分交


互, 坯通迥init_r如ll()函數控制 sys文件系统的rtkill対盔牙 硬件迸行操作 。与hci部分的
操作則主要通迥Socket完成, 打升Socket的create_hci_sock()函數核心如下所示:
int sk = socket ("l',F BLUETQOTH, SOCIS_RAW L ..J3罪 ROTO__JlCI);

此灶使用的AF_BLUETOOTH杯志, 就是盔牙仂汶族的杯涙。在套接字的调用中, 藍
牙仂讠又和IPv4 (Internet)仂讠又対皮 。
藍 牙的 Android 适 配 庠 libbluedroid.so 在 藍 牙系 统升栄时, 佘没置"hciattach"和
"bluetoothd"厲性, 打玕init.rc中定乂的hciattach和bluetoothd服各, 实瑰対藍牙初始化, 并
且升启守护迸程 。
Android在system/bluetooth中也提供了 一 些调试工具, 以方便调试。如bttest, 简单地
启停盔牙 调试工具 。

�14.2.4 盔牙的JNI部分
D-BUS是一 套被户泛釆用的IPC 通信机制。BlueZ以D-BUS为基絀,給其他部分提供
主要接口。 Android中的JNI和上展与BlueZ交互的手段也是D-BUS。
BlueZ 內核部分实瑰的仂汶, 迸行敖据亻賴俞, 比如 sco 、RFCOMM敖据等,直接使用
內核实瑰 仂汶, 直接輸出到藍牙芯片。
值得注意的是, A2DP部分是通迥Android的 Audio Interface直接整合到Android的
Audio Route部分 。 再将敷据通迥BlueZ的A2DP部分, 最終通迥L2CAP友送到硬件 。

1. android.bluetooth包中的基拙內容

android. bluetooth包相美的几令JN1文件如下。
e android_bluetooth_ BluetoothSocket.cpp: Socket核心奕BluetoothSocket的支持。
e android_bluetooth_ScoSocket.cpp: 基于RFCOMM的耳机 sco的Socket 支持。

241�
• O O Android 板级支持与硬件相美子系统

e android_bluetooth_HeadsetBase.cpp: 耳机的实現, 调用 RFCOMM 和 sco。


2. android.server 包中的基拙內容
android.server包相美的几令 JNI 文件如下。
e android_server_BluetoothService.cpp: 藍牙服各的核心,通迥 D-BUS 实瑰。
e android_server_BluetoothEventLoop.cpp: 藍牙事件循坏的姓理。
e android_server_:_BluetoothA2dpService.cpp: 作为 一令狹立服各的 A2DP 的实瑰。
除此之外,android_bluetooth_common.* 文件提供通迥 dbus 迸行操作的公用函敖,供几
介服各的 JNI 调用。

�14.2.5 盔牙的Java部分

1. 藍牙服勞部分
android.bluetooth 中的几令 AIDL 文件如下。
e IBluetooth.aidl: 藍牙的主要控制接口。
e IBluetoothCallback.aidl: 回调接口, 用于 RFCOMM 的侍榆。
e IBluetoothA2dp.aidl: A2DP 的接口。
e IBluetoothHeadset.aidJ: 耳机部分的接口。
BluetoothService 等 几 介 奕 是使用通 迥迏些远程 接 口釆实 現 的 , 它继承了
IBluetooth.Stub, 运行于 system_server 迸程中。 其中,通迥 Dbus 与盔牙的核心部分迸行交
互, 也通近 bluedroid 庠迸行使能和禁止藍牙的操作也包含于其中。
作为服各所实現的內容,将被 android.bluetooth 包中的各令奕调用米实現藍牙的各神功
能。 因此, 在各介皮用程序包中所调用的盔牙操作和藍牙服各分別运行于皮用程序迸程和
系统服各器 (system_server) 迸程。

2. android.bluetooth 中的 API 部分

盔牙 Java 框架扆的 API 部分是 android.bluetooth 包, 主要包括 BluetoothClass 、


BluetoothAdapter、 BluetoothSocket 和 BluetoothServerSocket 等几介奕。
BluetoothClass 奕表示藍牙没各的哭型, 其中主要包含如下方法:
public int getDeviceClass () //得到没各的哭型
public int getMajorDeviceClass () //得到主没各的突型
gublic boolean haJ;Serv:,i,c;_e (int servis;e) //没各是否包含某介服各

BluetoothClass.Device 为没各的具体奕型杯讠只; BluetoothClass.Service 为服各的奕型,


包 含 表示各 神服 各 的整 型常量; BluetoothClass.getDeviceClassO 得到没 各 的 奕 型由
BluetoothClass.Device 中 的整型厲 性 描 述; BluetoothClass.getMajorDeviceClass() 則由
BluetoothClass.Device.Major 的整型厲性描述。
BluetoothAdapter 奕表示藍牙的本地适配器,用于执行盔牙的基本功能,例如没各的友

現、 找到已經配対的 没 各列表, 給 BluetoothDevice 介 MAC 地址, 刨建
B luetoothServerSocket 釆瘟昕釆自其他没各的清求。 其中的 getDefaultAdapter。靜态方法用

, 242
第14章盔牙系统 ooe

于得到 一令默从的 BluetoothAdapter。


BluetoothAdapter 中几令控制没各的主要方法如下所示:
public boolean enable () II使能
public boolean disable () II禁止
public boolean startDiscovery () II升始岌現没各
public boolean cancelDiscovery () II取消友現没各
public boolean setfil.me (String name) II_没置没各的名稔

BluetoothDevice 粲表示 一 介远程的藍牙没各,利用 BIuetoothDevice 哭可以查洵迏令


没各的信息 getName ()、 getAddress ()、 getBluetoothClass ()等方法返回名稔、地址、癸
型等。
BluetoothAdapter 中的 getRemoteDevice()和 getBondedDevices() 两介方法的返回值就是
BluetoothDevice 炎型;前者表示从 MAC 地址荻得远程没各,后者表示荻得所有綁定没各
的列表。
BluetoothSocket 和 BluetoothServerSocket: 奕似基于 TCP 仂汶 Socket 和 ServerSocket,
用于盔牙敖据流的亻耜逸。

.在服各器端:BluetoothServerSocket 用于刨建 介到服各 Socket 的连接,蚩连接被

接受的时候,它将返回 令 BluetoothSocket。返回的 BluetoothSocket 用于管理迏介
连接。
.在客户端:使用 一介 BluetoothSocket 哭管理迏介连接。
在大部分情況下,它亻 是 RFCOMM癸型的。 n
BluetoothServerSocket 具有如下方法:
public BluetoothSocket accept() //接收
public BluetoothSocket accept(int timeout) //帶有超时的接收
public voi(! close <!a _ _ //栄困连接

BluetoothSocket 具有如下方法:
public void connect() II连接
public void close() II夫珝连接
public InputStream getlnputStream() //荻得榆入流
public OutputStr、earn getOutputStream() //荻得榆出流
publ:i.f'BluetoothDevic� getRemoteDevice() //荻得远程的没各

藍牙方面提供的接口絞多,从迏些接口的角度,调用的結果差昇不大,但是由于各介
Android 没各支持的盔牙規范存在差昇,某些功能在某些没各上可能不能工作。

14.3 BSP的結枸

Android 中藍牙系统的本地程序部分和框架扆都相対柝准。迏部分仅仅需要一些配置
即可。因此移植的主要內容是藍牙驱劫程序部分。
盔牙的驱幼部分包含針対硬件接口部分的 USB、 SDIO 和 UART 驱劫,迏部分比絞杯
准。有 一些基于 U ART 的盔牙芯片需要使用芯片特定的高速串口。另外,驱劫部分也包含

243�
•oo Android板级支持与硬件相美子系统

屯源管理,坯可能包含芯片的配置部分。由于硬件接口比絞杯准,也有很多盔牙芯片通迥
用户空伺的初始化代碼直接対芯片迸行写入,完成初始化部分。

�14.3.1 t:办讠文和驱劫程序
相比驱劫程序,藍牙的仂汶扆位于內核空伺的絞上尼。Linux 2.6.x的內核都已經集成
BlueZ。內核中的部分主要包含扔讠叉的底尼实現,并奐贤和硬件的通信。

1. 盔牙仂汶找
Linux 2.6 已經包含了 BlueZ 仂汶枝,迏是与硬件元美的仂汶,在內核中也厲于两絡仂
汶的 一 秤。 menuconfig的配置方法为NetworkingSupport=>Bluetooth subsystem support, 其
中包括了各秤仂汶支持,最重要的是HCI P、 L2CAP、 sco 、 RFCOMM、 BNEP等,一般
全部玕启即可。
典型配置結果的配置 文件(.config) 的內容如下所示:
CONFIG_BT=y
CONFIG BT L2CAP=y
# CONFIG BT ' L2CAP 'EXT FEATURES is not set
CONFIG BT SCO=y
CONFIG BT RFCOMM=y
CONFIG BT RFCOMM TTY=y
CONFIG BT BNEP=y
# CONFIG BT BNEP MC FILTER is not set
# CONFIG BT BNEP PROTO FILTER is not set
# CONFIG BT CMTP is not set
CONFIG BT RIQP=y

藍牙仂汶的配置方面,都是以CONFIG_BT玕失的。

2. 盔牙硬件连接部分
盔牙没各通常通迥USB、SDIO或UART迸行连接,其中UART为高速串口。
USB和SDIO 一 般是杯准的內核驱劫,而UART通常需要 高速串口,根据芯片不同而
各昇。迏部分的代碼在內核中主要分布在 driver/usb和 driver/serial路往中。
藍牙部分的驱幼程序在內核代碼的 driver/bluetooth目汞中。元讠全使用何神接口,都需
要玕启在內核中的驱劫支持, menuconfig 的配置路往为 NetworkingSupport=>Bluetooth
subsystem support=>Bluetooth device drivers, 迸入后可以根据需要升启所使用的硬件 连接方
式。呈然硬件平台有多秤,但是藍牙方面的巨別都集中在连接方式上,各秤不同的源代碼
通常以h ci_*.c为文件名。

3. 屯源管理
Android使用杯准的Linux rfkill 接口迸行盔牙芯片屯源管理。使用送秤方式需要实現

令平台没各(platform_ device), 并实瑰rfkill的控制逆緝。
rfkill的接口在內核如下失 文件中定叉: include/linux/rfkill.h。
対藍牙來説,简单的屯源管理只需要美注其中几介部分即可。首先需要为盔牙没各分

, 244
第14章 盔牙系统 ooe

配控制結杓, 內容如下所示:
struct rfkill * must check rfkill alloc(const char *name,
struct device *parent, const enum rfkill_type type,
const struct rfkill_ops *ops, void *op_s_data);

其中重要的几令域: t ype需要填入RFKILL_TYPE_BLUETOOTH, ops需要填入指向


r加ll_ops結杓的指針。rfkill_ops实現対没各的操作, 定叉为:
struct rfkill_ops (
void (*poll) (struct rfkill *rfkill, void *data);
void (*query) (struct rfkill *rfkill, void *data);
int (*set_block) (void *data, bool blocked);
);

简单的屯源操作,仅需要实現 set_block接口。垓 接口实現中,皮垓实瑰対盔牙 没各的


具体升美操作。部分藍牙芯片只要停止榆入时紳, 即可迸入低功耗模式, 可以在此玕美时
紳;其他 一些 盔牙芯片需要再次迸行 屯源升美操作。

- 提示: 在板级支持中实現rfkill后, 用户空f司通辻sys文件系统的文件迸行控制。

4. HCI 配置部分
硬件 接口和藍牙 HCI (主机控制 接口)仂汶都比絞杯准, 大部分藍牙芯片已經采用在
用户空冏迸行 配置的方法。

�14.3.2 本地代碼的配置部分
本地代碼部分,主要完成 配置性的一些工作。主要包括盔牙芯片的屯源管理、 初始化,
以及藍牙服各的配置等。
第一,12,: rfkill与屯源管理
rfkill奐靑在內核中 盔牙部分的使能和禁止,一般将対皮到藍牙相失引朐,每令系统的
結杓不同, 但统 一到用户空冏都是sys文件系统中的rfkill文件。Android中的libbluedroid
调用r加II的接口迸行 屯源管理控制。
第二 ,8: hciattach 初始化
盔牙系统的真正初始化 在hciattacb坏节中完成, 包括固件的下裁、 洋细的配置等, 确
保盔牙芯片能得到 正确的初始化 指令。 在init.rc中的bciattach服各作为单次运行的服各,
由Java服夯中的盔牙服各 通迥bluedroid庠执行。也可以在命令 行中 执行 bciattach, 初始化
的迥程中, 就可以通迥hciconfig査看HCI通信的细节, 查看执行這程中的信息。
第三熹: bluttoothd守护迸程
盔牙系统的初始化完成后,init.rc中的bluetootbd作为守护迸程运行。 在正常运行的迥
程中, 各令系统之冏的差昇不大,但是 一些藍牙系统的特性有配置 方面的差昇。 在目柝系
统 正常运行 迥程中, 可以通迥hciconfig迸行 配置, 使用hcitool工具査看盔牙芯片狀态, 使
用rfcomm工具调试RFCOMM等工具。

245�
• O O Android 板级支持与硬件相美子系统

14.4 Android 4.2的盔牙系统

在 Android 系统的友展這程中, 盔牙部分的升级玕始只是髄着 Linux 藍牙实瑰 BlueZ


升级, 并平行地增加功能。 Android 2.3 、 Android4.0 和 Android4.1 版本的藍牙系统癸似,
到了 Android4.2 版本, 盔牙部分友生比絞大的変化。

�14.4.1 系统結杓
Android4.2 的藍牙系统的主要変化体瑰在以下几令方面。
國增加藍牙的硬件模玦作为与硬件交互的扆次。
0藍牙系统用户空阅的庠不再使用 BlueZ。
國藍牙服各部分的功能移到 BlueTooth 的皮用程序包 (apk) 中。
Android 4.2 的藍牙部分的結杓如囷 14-3 所示。

盔牙皮用 I BlueTooth包 AdapterService


Java/並用尼

Java框架展

藍牙模炔
C框架J炅
.............
內核
盔牙仂汶尼
鬪」
-
·
··
·
··
·

藍牙的rfkill 盔牙驱劫(UART, USB..)

囹14-3 Android 4.2的藍牙部分的結杓

�14.4.2 盔牙硬件模玦
Android 4.2 的藍牙硬件模玦是一 介杯准的硬件模玦, 相比其他硬件模玦, 藍牙的硬件
模坎比絞夏朵,由 hardware/libhardware/include/hardware/ 目汞中的 bluetooth.h 的藍牙模坎文
件, 以及 bt_av.h 、 bt_hf.h 、 bt_hh.h 、 bt_hl.h 和 bt_pan.h 各介功能文件組成。

1. 盔牙模玦的主入口 bluetooth.h

bluetooth.h 中藍牙没各的定又如下所示:
typedef struct {
struct hw device t common;
const bt_interface_t* (*get bluetooth interface)(); // 荻得盔牙的接口

�246
第14章 盔牙系统 ooe
) bluetooth_gevice_t;

bt_ interface_t 是藍牙模玦的主要调用接口, 結杓如下所示:


typedef struct {
size_t size; //bt interface t結杓体的大小
int (*init)(bt callbacks t* callbacks ); II初始化, 没置回调的句柄
int (*enable)(void); //使能
int (*disable)(void); //禁止
void (*cleanup)(void); //消除
// . . . . . . 省略: 荻取和没翌adapter (适配器)和remote_device (远程没各)的函致指針
int (*get_remote_services)(bt_bdaddr_t *remote_addr); //荻取服各友現仂汶(SOP)
int (*start_discovery)(void); //玕始対远程没各的及現
int (*cancel_discovery)(void); //取消対远程没各的友現
int (*create_bond)(const bt_bdaddr_t *bd_addr); //建立盔牙的綁定
int (*remove_bond)(const bt_bdadd互_t *bd_addr); //刪除盔牙的綁定
int (*cancel_bond)(const bt_bdaddr_t *bd_addr); · //取消藍牙的綁定
// . . . . . . 省略: PinKey, SSP Reply和OUT的相哭函敷指針
J bt interface t;

bt_ interface_t 是上尼调用藍牙硬件抽象扆的主要接口, 在盔牙系统运行的迥程中, 实


际上主要是下展调用上展的迥程, 因此回调函敖是核心机制。 由调用者实現 bt_callbacks_t
之后, 在 bt_interface_t 的初始化阶段作为參敖亻考入。
各介相哭回调函數的炎型如下所示:
typedef void (*adapter_state_changed_callback)(bt_state_t state); //没各器狀态
typedef void (*adapter_properties_callback)(bt_status_t status,
int num_properties, bt_property_t *properties); //荻取和没翌屑性
typedef vo這(*remote_device_properties_callback)(bt_status_t status,
bt_bdaddr t *bd addr, int num_properties,
bt property t *properties),· II荻取和没置远程没各的厲性
typedef void (*device_found_callback)(int num_properties,
bt_property_t *properties); II没各及現
typedef void (*discovery state_changed_callback)(bt_discovery_state_t state);
II省略: PinKey和SSP的回渭哭型
typedef void (*bond_state_changed_callback)(bt_status_t status,
bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state); II綁定
typedef void (*acl_state_changed_callback)(bt_status_t status,
bt bdaddr t *remote 'bd' addr, bt acl state t state); IIACL连接狀态
typedef enum ( ASSOCIATE_JVM, DISASSOCIATE_JVM } bt_cb_thread evt;
typedef void (*callback thread event)(bt_cb_thread_evt evt ); II紱程联系到虛似机
II省略: 其他盔牙的函效指針

盔牙系统的回调指針中, 有些是作为函敖调用的昇步返回信息使用的, 坯有的是綫程


联系到Java虛姒机相哭的。
bt_callbacks_t 就是由以上回调函數的指針組成, 如下所示:
typedef struct {
size_t size; IIbt callbacks t結杓体的大小
adapter_state_changed_callback adapter_state_changed_cb;
adapter_properties_callback adapter_properties_cb;
remote_device_properties_callback remote_device_properties_cb;
device found callback device found cb;
discovery_state_changed_callback discovery_state_changed_cb;
pin_request_callback pin_request_cb;
ssp_rELques辶c;:allback ssp_Jeques辶cb;

247�
•oo Android板级支持与硬件相美子系统

bond_state_changed_callback bond_state_changed_cb;
acl_state_changed_callback acl_state_changed_cb;
callback thread event thread evt cb;
dut mode recv callback dut mode recv cb;
I bt_callbacks_t;

迏神由<功能>_interface_t癸型的调用結杓 和<功能>_callbacks_t癸型回调結枸組成的
接口, 不仅在此姓的藍牙模坎中使用, 也在盔牙的各今子功能接口中使用。
bluetooth.h中定乂的几介接口的名稔(此灶杯为ID)如下所示:
#define BT_PROFILE_HANDSFREE_ID "handsfree"
#define BT_PROFILE_ADVANCED_AUDIO一ID "a2dp"
#define BT PROFILE HEALTH ID "health"
#define BT_PROFILE_SOCKETS一ID "socket"
#define BT PROFILE HIDHOST ID "hidhost"
#define BT 'PROFILE" PAN 'ID "pan"

有些內容名稔基本和盔牙既范(profile)相対皮的。

2. 用于藍牙套接字的bt_sock.h
bt_sock.h是藍牙套接字的接口, 用于rfcomm、 sco 和L2CAP几秤奕型, 与藍牙甥范
中定叉的內容相対皮。
btsock _interface_t結杓如下所示:
typedef struct (
size t size; // btsock interface t結杓的大小
bt_status_t(*listen) (btsock_type_t type,

int* sock_fd, int flags);



const char* service_name, const uint8 t* service uuid, int channel,
//監昕套接字, 表不rfcomm的UID和通道
bt_status_t(*connect) (const bt_bdaddr_t *bd_addr,
btsock_type_t type, const uint8 t* uuid,
int channel, int* sock fd, int flags); // 连接到远程没各
) btsock_interface_t;

listen和connect是杯准Socket函數的封裝, 其中的 connect btsock_type_t是 一 令整敖,


対皮 BTSOCK_RFCOMM (1)、 BTSOCK_SCO(2)和BTSOCK_L2CAP (3)几介枚举值。

3. 用于藍牙音颜、 视頻的bt_av.h
bt_av.h� 文件用于盔牙音颜和视颜, 主要灶理的是耳机方面的功能。
表示接口的 btav_interface_t結杓如下所示:
typedef struct {
s1.ze t size; // btav_interface t维杓的大小
bt_status_t(*init) ( btav_callbacks_t* callbacks ); //初始化注朋回调
bt status"t(*connect)( bt bdaddr t *bd addr ); II连接到耳机
bt_status_t(*disconnect)( bt_bdaddr_t *bd_addr ); //新升到耳机的连接
void (*cleanup)( void ); //清除
J btav_interface_t;

表示回调的 btav_callbacks_t結杓如下所示:
typedef void(* btav_connection_state_callback)(btav connection state t state,
bt bdaddr t *bd addr); II盔牙音视颜的连接狀态
typedef void(* btav_audio_state_callback)
(btav_audio_state_t state,

�248
第14章 盔牙系统 ooe
bt bdaddr t b* d addr); II音颜狀态的変化
typedef struct {
size t size; // btav_callbacks_t結杓的大 4
btav_connection_state_callback connection_state_cb;
btav audio state callback audio state cb;
} btav callbacks t;

盔牙的音颜有考 n的盔牙規范提供, 此灶只是迸行夏制的耳机连接方面的外理, 连接


狀态有已經连接、 已經新升连接、 连接中和斷玕连接中4秤。

4. 用干藍牙HandFree的bt_hf.h
bt_hf.h用于盔牙HandFree (免提), 此接口的內容比絞夏朵, 包括连接、 音颜、 语音
沢別、 音量控制和狀态変化几介方面的內容。
bthf_interface_t結杓的內容如下所示:
typedef struct {
size t size; // bthf interface t結杓的大小
bt_status_t ('* init)(bthf_callbacks_t * callbacks ); //初始化没置回调
bt_status_t (*connect)(bt_bdaddr_t b * d_addr ); //连接
bt_status_t (*disconnect)(bt_bdaddr_t b * d_addr ); //新升连接
bt_status_t (*cpnnect_audio)(bt_bdaddr_t b * d_addr ); II建立音颜的连接
bt_status_t (*disconnect_audio)(bt_bdaddr_t *bd_addr ); II新升音颜的连接
bt_status_t (*start_voice_recogni户on)( ); II升始语音涙別
bt_status_t (*stop_voice_recognition)( ); II停止语音泯別
bt_status_t (*volurne_control) ( bthf_volurne_type_t type, int volume); II音且
bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state,
bthf_service_type_t svc_type, int signal, int batt_chg); II狀态通知
II 省略: COPS 、 CIND 、 AT、 CLCC命令的相栄接口
bt_status_t (*phone_state_change) ( int nurn_active, int nurn_held,
bthf_call_state_t call_setup_state, const char *number,
bthf_call_addrtype t type); II屯话狀态改変, 另碼, 狀态
void (*cleanup)(void ); //清除
bthf in:te.x:_(9_ce 七

由于此灶调用的回调比絞多, 奕型的回调結杓如下所示:
typedef void{ * bthf_connection_state_callback){bthf connection state t state,
bt bdaddr t *bd addr); //藍牙连接狀态
typedef void{ * bthf_audio_state_callback){bthf audio state t state,
bt_bdaddr_t *bd_addr); //音颜狀态
typedef void{ * bthf vr cmd callback) {bthf_vr_state_t state); II语音沢別
II 省略: AT命令和其他命令的回调突型
typedef void{ * bthf_volume_cmd_callback){bthf_volume_type_t type, int volume);
typedef void{ * bthf_unknown_at_cmd_callback){char a * t_string); II不明命令回调
typedef void{ * bthf_key_pressed_cmd_callback){); II按鍵的車件
typedef struct {
size t size; // bthf callbacks t結杓的大小
bthf connection state callback connection state cb;
bthf_audio_state_callback audio_state_cb;
bthf vr cmd callback vr cmd cb;
bthf answer call cmd callback answer call cmd cb;
bthf hangup call cmd callback hangup_call_cmd_cb;
II 省略: AT命令和其他命令的回调函敷指钳
bthf_unknown_at_cmd_callback unknown_at_cmd_cb;
bthf_keyyressed_cmd_callback key_pressed_cmd_cb;
) bthf_、 allbacks_t;

249�
•oo Android 板级支持与硬件相美子系统

根据盔牙仂汶枝的結杓,此姓的HandFree功能实际上是対底扆功能的封裝,为了方便
上扆更容易使用,因此将主要的功能組纸成了回调函數的形式。

5. 用于盔牙的HID的Hostbt_hh.h

Host bt_hb.h文件用于HID (Human Interface Device, 人机接口没各) 的主机端操作,



HID也是藍牙 介杯准的規范。
bthh_interface_t結杓的內容如下所示:
typedef struct {
size t size; // bthh interface t結杓的大小
bt status t (*init) (bthh_callbacks_t * callbacks ); // 初始化并注朋回调
bt_status_t (*connect) (bt_bdaddr_t *bd_addr); //连接
bt_status_t (*disconnect) (bt_bdaddr_t *bd_addr ) ; //靳玕连接
bt status t { v
* irtual unplug) {bt_bdaddr_t *bd_addr); //虛似的UnPlug {VUP)
II省略: 一些荻取和没置函致指針
bt_status_t (*send_data) (bt_bdaddr_t *bd_addr, char * data);//友送敷据
void (*cleanup) (void ); //清除
} bthh_interface_t;

控制的接口大多數是輔助性的,virtual_unplug函敖指針灶理虛姒插拔的功能。 由于此
外提供的是HID主机的功能, 主要的信息将从回调亻軒給上尼。
回调的炎型和結杓如下所示:
typedef void {* bthh_connection_state_callback) { //连接狀态的回调
bt_bdaddr_t *bd_addr, bthh_connection_state_t state);
typedef void { * bthh_virtual_unplug_callback) {bt_bdaddr_t *bd_addr,
bthh status t hh status); //虛姒拔除
typedef void (* bthh_h這_info_callback) ( bt_bdaddr_t *bd_addr,
bthh hid info t hid info);
II省略:其他的回调炎型
typedef struct {
size t size; II bthh_callbacks_t結杓的大小
bthh_connection_state_callback connection_state_cb;
bthh h這 info callback hid info cb;
bthh_protocol_mode_callback protocol_mode_cb;
bthh idle time callback 這le time cb;
bthh_get_report_callback get_report_cb;

一 - ""' " --
bthh_virtual_unplug_callback
} bthh callbacks t;
virtual_unplug_cb;

bthh _hid_info_callback回调奐靑将信息亻耜逸給上展, 以bthh-hid-info- t 結杓为參數,


其中包括了ID和控制碼方面的內容。

6. 用于藍牙HDP的bt_hl.h

bt_hl.h用于HDP (BluetoothHealth Device Profile, 藍牙健康没各規范),主要是将藍


牙健康没各(通常是伟感器) 的信息伟逸給上扆(既范中稔为皮用)。
bthl_interface_t結杓的內容如下所示:
typedef struct {
size t size; II bthl interface t結杓的大小
bt status t (*init} (bthl callbacks t * callbacks } ; II 初始化,没置回调
bt_status_t (*re_gister_application}_ (bthl_reg_param_,t *p_reg__pJiramL

�250
第14章 藍牙系统 ooe
int *app_id); //注朋客户端的皮用
bt_status_t (*unregister_application) (int app_id); II注铜客户端的皮用
bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr,
int mdep_cfg_index, int *channel_id); II连接到通道
bt_status_t (*destroy_channel)(int channel_id); II刪除到通道的连接
void (*cleanup)(void); I I清除
) bt旦int兔rface_t;

此灶的app_id表示HDP使用者(客户端)的表示, 注冊之后可以连接到某令通道,
迸而通迥回调荻取HDP没各的信息。
回调的奕型和結杓如下所示:
typedef vo這(* bthl_app_reg_state_callback)位nt app_id,
bthl_app_reg_state_t state);
typedef vo這(* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr,
int mdep_cfg_index, int channel三d, bthl_channel_state_t state, int fd);
typedef struct {
size t size; // bthl callbacks t結杓的大小
bthl_app_reg_state_callback app_reg_state_cb;
bthl channel state callback channel state cb;
} bthl_callbacks_t;

HDP的回调包括了狀态的回调和通道的回调, 伟逸敖据的是 令虛姒的文件描述符

(fd), 客户端按照 介格式从文件描述符中淩取 信息。

7. 用于藍牙PAN的bt_pan.h

bt_pan.h文件用于藍牙(Personal Area Network, 介人局域两)方面的功能。


btpan _interface_t結杓的內容如下所示:
typedef struct (
size t size; // btpan interface t結杓的大小
bt status t (五nit)(const btpan callbacks t* callbacks); //初始化并注朋回调
bt status t (*enable)(int local role); //使能local_role
int (*get local role)(void); II荻得local role
bt status t (*connect)(const bt bdaddr t *bd addr, //连接到某介地址
int local_role, int remote_role);
bt status t (*disconnect)(const bt bdaddr t *bd addr); //停止连接
void (*cleanup)(void); //清除
}莖氬int包:face_t;

在使能阶段需要亻考入 令特定的表示整數的职責, 没各返回的信息則将佘从回调中亻钅
回。 回调的哭型和結杓如下所示:
typedef void (*btpan connection state callback)(btpan connection state t state,
bt status t error, const bt bdaddr t *bd addr,
int local_role, int remote_role);
typedef void (*btpan_control_state_callback)(btpan_control_state_t state,
bt_status_t error, int local_role, const char* ifname);
typedef struct (
size_t size; // btpan_callbacks_t結杓的大小
btpan_control_state_callback control_state_cb;
btpan_connection_state_callback connection_state_cb;
} btpan_callbacks_t;

此灶除了连接的回调之外, 坯有控制回调, 伟回的內容由藍牙的PAN烛范來描述。

251�
•oo Android板级支持与硬件相美子系统

�14.4.3 盔牙系统的本地展部分
Android4.2 藍牙系统的本地扆的內容依然在external/bluetooth/目呆中,但没有了BlueZ
部分,只有 bluedroid目汞,提供Android 本地展的支持。
根据main目汞中的Android.mk可以生成bluetooth.default.so幼态庠,也就是盔牙硬件
模玦的默从实現,放置于目才示系统的/system/lib/hw目汞中。
bluetooth.default.so劫态庠包括如下几介子目呆的內容。
e main/*: 藍牙模玦的入口。
e btif/src/btif_<名稔> . *:包括藍牙模玦核心、Socket和各秤功能接口的实現。
e btif/co/bta_<名杯>_CO.C: 盔牙的 sbc模玦的实瑰。
e embdrv/sbc/*: 盔牙的 sbc模玦的实現。
根据audio_a2dp_hw目汞中的Android.mk文件,将生成audio.a2dp.default劫态庠,也
就是藍牙音颜的默从实現,放置于目才示系统的/system/ lib/hw目汞中。

由于 藍牙系统在用户空l 司的內容奕似,一般的系统使用此姓的默从实珗即可。

�14.4.4 BlueTooth包
Android 4.2 的藍牙部分将 Java 框架尼中的很多功能移到了盔牙的皮用程序包中,即
package/app/BlueTooth目汞生成的BlueTooth 皮用程序包中。由此Android4.2的BlueTooth
包也担奐了更多的框架尼的职責,并且具有了JNI 部分。
BlueTooth 包 名为 com.android.bluetooth, 其中 btservice.AdapterService 奕通迥 Service
組件的方式实瑰了框架尼定乂的IBluetooth接口(aidl)。

-提示: Android4.2之前的籃牙系统中,IBluetooth接口在系统服夯器中实瑰。

jni目泵中的內容将生成名稔为libbluetoothjni.so的JNI 庠,作为btservice包中各介部
分的本地部分,它亻I']实际上通迥调用藍牙的硬件模玦庠 实瑰。
com_ android_bluetooth _btservice_AdapterService.cpp文件是主要入口 AdapterService的
JNI部分,其中的几介注朋函數如下所示:
int register_com_android_bluetooth_hfp(JNIEnv* env);
int register_com_android_bluetooth_a2dp(JNIEnv* env);
int register_com_android_bluetooth_hid(JNIEnv* env);
int register_com_android_bluetooth_hdp(JNIEnv* env);
int register com android
. ' bluetooth
� pan(JNIEn又 "!' env);

上述的几介函敖用于 藍牙各介功能接口的实瑰和注朋。
a2dp、扣d、hfp、hdp和 pan 几介子包中的內容就是盔牙各介功能模玦的实現,也通逍
JNI 部分调用藍牙硬件模玦的各令工程接口米实瑰。

�252
第14章 藍牙系统 ooe

14.5 BSP的实現

�14.5.1 Nexus One系统的盔牙实瑰


MSM 的 Nexus One 系统的藍牙部分使用了 UART 连接, 因此需要包括驱劫代碼的
如 vers/bluetooth/ 目呆中的 hci_h4.c 、 hci_ldisc.c 、 hci_ll.c 和 hci_uart.c 等几今文件。

MSM 系统的盔牙芯片通迥 HS-UART 与 MSM 灶理器相连, 迏是 秤串口的驱劫, 代
碼路往为: driver/serial/msm_serial_hs.c。在用户空阅中,将生成没各文件的节煮 /dev/ttyHSO。
內核中藍牙相美的另外 一 部分內 容是屯源管理部分代碼 , 迏部分 的代碼 路 往 在

arch/arm/mach-msm 目汞的 board-mahimahi-r加 11.c 文件中。 迏也是 今平台没各, 名杯为
"mabimabi_rfkill", 作为藍牙的 r加 II 实瑰,在探測的初始化部分中完成対 r加U接口的注朋,
內容如下所示:
static int mahimahi_rfkill_probe(struct platform_device *pdev) {
int re = O;
bool default_state = true; //默从的狀态为哭
re = gpio_request(MAHIMAHI_GPIO_BT_RESET_N, "bt_reset");
if (rc)goto err_gpio_reset;
re = gpio_request(MAHIMAHI_GPIO_BT_SHUTDOWN_N, "bt_shutdown");
if (rc)goto err_gpio_shutdown;
bluetooth_set_power(NULL, default_state); //没咒屯源部分
bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH,
&mahimahi_rfkill_ops, NULL);
II省略錯课灶理內容
rfkill_set_states(bt_rfk, default_state, false);
re = rfkill_register(bt_rfk); II注冊所实現的盔牙 RFKILL
if (re) goto err_rfkill_reg;
return 0;
II省略錯课赴理內容

以上的 bt_rfk 是一 介由 rfkill_alloca頲)函數分配出米的 rfkill 癸型的結杓体。 完成初始


化后, 使用 rfkill_registerO 将其注朋到系统中。
bluetooth_set_powerO 函數被没置成 rfkill 結杓的 toggle_radio 指針, 实瑰的藍牙升美在
工作, 內容如下所示:
static int bluetooth_set_power{void *data, enum rfkill_state state) {
switch {state) {
case RFKILL STATE UNBLOCKED: // RFKILL 的非阻塞狀态
gpio_configure{MAHIMAHI_GPIO_BT_RESET_N,
GPIOF DRIVE OUTPUT I GPIOF OUTPUT HIGH);
gpio_configure{MAHIMAHI_GPIO_BT_SHUTDOWN_N,
GPIOF_DRIVE_OUTPUT I GPIOF_OUTPUT_HIGH);
break;
case RFKILL STATE''SOFT BLOCKED: // RFKILL 的軟阻塞狀态
gpio_configure(MAHIMAHI_GPIO_BT_SHUTDOWN_N,
GPIOF_DRIVE_OUTPOT I GPIOF_OUTPUT_LOW);
gpio_configure(MAHIMAHI_GPIO_BT_RESET_N,
GPIOF DRIVE_OUTPUT I GPIOF_OUTPUT_LOW);

253�
•oo Android 板级支持与硬件相美子系统

break;
default:
pr_err("急s: bad rfkill state料\n", _func_, state);

return 0;

bluetooth_set__power 中通迥対 GPIO 的操控控制藍牙芯片的升美是対硬件的操作。內容


的本原則是控制藍牙相美的引脾, 并通迥r如11 提供 sys 文件系统作为到用户空囘的接口。

�14.5.2 Nexus S系统的盔牙实瑰


三星的 Nexus S 系 统的盔牙部分使用了 UART 连接, 因此需要包括驱幼代碼的
drivers/bluetooth/ 目泵中的 hci_h4.c 、 hci_ldisc.c 、 hci_ll.c 和 hci_uart.c 等几介文件。
三星的 Nexus S 盔牙部分的 rfkill 的实瑰在垓平台的板级支持中, 其代碼路往为:
arch/arm/mach-s5pv210/herring-r加11.c, 迏里定叉了 - 今平台没各的名稔为 "bt_r 加11"' 其探
測函敖如下所示:
static int _init herring_rfkill_probe(struct platform_device *pdev) {
int irq;
int ret;
wake_lock_init(&rfkill_wake_lock, WAKE_LOCK_SUSPEND, "bt_h5'st_wake");
ret = gpio request(GPIO_WLAN_BT_EN, "GPB");
//省略錯课灶理
ret = gpio request(GPIO BT nRST, "GPB"); // GPIO的灶理
II省略措课灶理
irq = IRQ BT HOST WAKE; // BT控制器的中新
ret = request_irq(irq, bt_host_wake_irq_handler,
IRQF_TRIGGER_RISING I IRQF_TRIGGER_FALLING,
"bt_host_wake一irq_handler", NULL);
II省略借课灶理
disable_irq(irq}; II禁止中新
bt_rfk = rfkill_alloc(bt_name, &pdev->dev, RFKILL_TYPE_BLUETOOTH,
&bt_rfkill_ops, NULL);
//省略借课灶理
rfkill init sw state(bt rfk, O);
ret = rfkill register(bt rfk); //注朋rfkill
//省略措课灶理
rfkill set sw state(bt rfk, 1);
bluetooth_set_power(NULL, RFKILL_USER_STATE_SOFT_BLOCKED);
return ret;
//省略錯课赴理

此灶定又的 rfkill_ops 操作和其中的实現函數如下所示:


static int bt rfkill set block(void *data, bool blocked) {
unsigned int ret = 0;
ret = bluetooth_set_power(data, blocked?
RFKILL USER STATE SOFT BLOCKED : RFKILL USER STATE UNBLOCKED);
return ret;

static const struct rfkill_ops bt_rfkill_ops = {


.set block = bt rfkill set block,
l;

�254
第14章蓝牙系统 ooe

bt_rfkill_set_block是rfkill的set_block操作。bluetooth_ set_power()函数用于对蓝牙电
源的操作,实现的内容如下所示:
s t a t i c i n t bluetoo七h_set_power(void *data, enum rfkill_user_states state)(
i n t r e t = 0;
int irq;
irq = IRQ BT HOST WAKE; // BT 控制器的中断
switch (state) (
case RFKILL USER STATE UNBLOCKED: // rfkill 的非阻塞状态
s3c_setup_uart_cfg_gpio(O};
if (gpio_is_valid(GPIO_WLAN_BT_EN}}
gpio_direction_outpu七(GPIO_WLAN_BT_EN, GPIO_LEVEL_HIGH};
if (gpio_is_valid(GPIO_BT_nRST}}
gpio_direction_output(GPIO_BT_nRST, GPIO_LEVEL_LOW);
s3c_gpio_setpull(GPIO_WLAN_BT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_WLAN_BT_EN, GPIO_LEVEL_HIGH);
s3c_gpio_slp_cfgpin(GPIO_WLAN_BT_EN, S3C_GPIO_SLP_OUT1);
s3c_gpio_slp_setpull_updown(GPIO_WLAN_BT_EN, S3C_GPIO_PULL_NONE};
msleep(lOO);
s3c_gpio_setpull(GPIO_BT_nRST, S3C_GPIO_PULL_NONE};
gpio_set_value(GPIO_BT_nRST, GPIO_LEVEL_HIGH};
s3c_gpio_slp_cfgpin(GPIO_BT_nRST, S3C_GPIO_SLP_OUT1};
s3c_gpio_slp_setpull_updown(GPIO_BT_nRST, S3C_GPIO_PULL_NONE);
msleep(50);
re七= enable_irq_wake(irq);
//省略错误处理
enable_irq (irq); · / /使能IRQ
break;
c a s e R F K I L L USER STATE SOFT BLOCKED: // rfkill 的软件阻塞状态
ret = disable_irq_wake(irq);
//省略错误处理
disable_irq (irq); //禁止 I RQ
wake_unlock (&rfkill_wake_lock);
s3c_gpio_setpull(GPIO_BT_nRST, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_BT_nRST, GPIO_LEVEL_LOW);
s3c_gpio_slp_cfgpin(GPIO_BT_nRST, S3C_GPIO_SLP_OUTO);
s3c_gpio_slp_setpull_updown(GPIO_BT_nRST, S3C_GPIO_PULL_NONE);
if (gpio_get_value(GPIO_WLAN_nRST) = = 0) (
s3c_gpio_setpull(GPIO_WLAN_BT_EN, S3C_GPIO_PULL_NONE);
gpio_set_value(GPIO_WLAN_BT_EN, GPIO_LEVEL_LOW);
s3c_gpio_slp_cfgpin(GPIO_WLAN_BT_EN, S3C_GPIO_SLP_OUT0);
s3c_gpio_slp_setpull_updown(GPIO_WLAN_BT_EN, S3C_GPIO_PULL_NONE);

break;
default:

return 0;

对于蓝牙部分的电源操作,此处操作了串口UART和GPIO, 这是三星的Nexus S 平 台
的特别硬件操作。由于用户空间对r加11可以多处进行,因此在实现中也使用wakelock进
行了加锁操作。

�14.5.3 Galaxy Nexus系统的蓝牙实现


三星的Galaxy N e x u s 系 统 的 蓝 牙 部 分 使 用 了 U A R T 连 接 , 因 此 需 要 包 括 驱 动 代 码

255�
•oo Android板级支持与硬件相关子系统

如vers/bluetooth/目录中的bci_h4.c、hci_ldisc.c、hci_ll.c和hci_uart.c等几个文件。
Galaxy Nexus系统的蓝牙系统继承自Tuna板的实现,板级支持的蓝牙部分的代码路径
为: arch/arm/mach-omap2/board-tuna-bluetooth.c。
蓝牙rfkill部分的内容如下所示:
static struct rfkill *bt_rfkill;
s t a 巨 c struct regulator *clk32kaudio_reg; II V o l t a g e & C u r r e n 七 R e g u l a t o r
static int b c m 4 3 3 0 _ b t _ r f k i l l _ s e t _ p o w e r ( v o i d *data, bool blocked) (
if (! blocked) (
if (clk32kaudio_reg && !bt_enabled)
regulator_enable(clk32kaudio_reg); //控制CLK32K的引脚使能
gpio , set

v a l u e ( B T R E G GPIO, 1); II设宵GPIO的引脚
gpio s e 七 v a l u e ( B T R E S E T GPIO, 1);
} else {
gpio s e 七 v a l u e ( B T R E S E T GPIO, 0); //设置GPIO的引脚
gpio_set_value(BT_REG_GPIO, 0);
if (clk32kaudio_reg && bt_enabled)
regulator_disable(c.J,k32kaudio_reg); //控制CLK32K的引脚禁止

bt_enabled = !blocked;
return 0;

static c o n s t s t r u c 七 r f k i l l _ o p s bcm4330_bt_rfk ll_ops = { // rfkill的结构


.set_block = bcm4330_bt_rfkill_set主ower,
);

在蓝牙的rfkill操作过程中,除了进行GPIO的设置之外,还需要控制CLK32K, C L K 3 2 K
即为对外设的32K时钟输出脚,在Linux内核中使用电压电流调整器C Voltage/Current
Regulator)的驱动程序。
蓝牙的r加11内容的probe过程如下所示;
static int bcm4330_bluetooth_probe(s七ruct p l a t f o r m _ d e v i c e *pdev) {
int r e = O;
int ret = 0;
re = gpio_request(BT_RESET_GPIO, "bcm4330_nreset_gpip"); // G P I O 复 位
if (unlikely(rc)) { return r e ; }
re = gpio_reques七(BT_REG_GPIO, "bcm4330_nshutdown_gp上o"); // G P I O 关 闭
if (unlikely (re)) { gpio_free (BT_RESET_GPIO); return re; }
e l k 3 2 k a u d i o _ r e g = regulator_get(O, "elk32kaudio"); //得到regulator结构
if (IS_ERR(elk32kaudio_reg)) { e l k 3 2 k a u d i o _ r e g = NULL; }
gpio一中ree巨on_output(BT_REG_GPIO, 1); //为蓝牙控制设置GPIO为输出
gpio_diree七ion_outpu七(BT_RESET_GPIO, 1);
b t _ r f k 过 l = r f k 过 l _ a l l o e ("bem4330 Bluetooth", &pdev->dev, //分配rfk过1结构
RFKILL_TYPE_BLUETOOTH, &bem4330_bt_rfkill_ops, NULL);
II省略错误处理
rfkill_set_sta七es(bt_rfkill, true, false); //设置rfkill的状态
re = rfkill register(bt_rfkill);
//省略错误处理
ret = b c m b t 1pm init(pdev);
//省略错误处理

由此可见,Tuna平台的蓝牙初始化过程中,同样需要对CLK32K和GPIO的操作。

�256
第15章
定位系统

15.1 定位系统的概述

Android的定位系统具有完整架构,并采用不同的手段来定位。GPS (基于全球定位系
统)属于卫星导航系统,需要特定的硬件接收来自卫星的数据,Android的定位系统主要使
用GPS设备。AGPS的含义为Assisted G P S (辅助GPS), 可以通过移动电话、IP网络等手
段实现定位。

Android定位系统对上层的Java应用实现统 的接 口,可 以被 G oogle 地 图等与定位相
关的应用所使用。GPS系统的相关内容如表15-1所示。

表 15-1 GPS 系统 的相 关 内容
Android层次 GPS系统部分 描 述
硬件层次 GPS设备,通常通过UART控制
操作系统层 表示为TTY串口驱动形式 一般是UART的驱动程序
硬件抽象层 GPS硬件模块 标准硬件模块
本地框架层 GpsLocat1onProv1der的JNJ 被系统服务器的Java部分调用
Java框架层 GpsLocationProv1der, LocationManagerService 具有内部包和外部包
Java层的API LocationManager 屈于非执行的AP!

15.2 定位子系统的结构

�15.2.1 总体结构
Android定位系统自下而上包含了驱动程序、硬件抽象层、JNI部分、Java框架层等几
个层次,如图15-1所示。
• O O Android板级支持与硬件相关子系统

定位相关应用(Google Map等)
Jav瘟用

LocationManagerServ1ce android. location

-—--,
com.android.server.location
GpslocationProvider
com.android.internal.location
Java框架

GPS定位的JNI

GPS定位适配层接口

GPS定位适配层(硬件模块)
本地框架层

Linux内核层 GPS驱动 移植 i
部公___ J

图 15- 1 定位系统部分的结构

自下而上,定位系统的几个层次如下所示。
(1)驱动层: GSP驱动程序
该层主要实现GPS等的硬件驱动,大部分GPS通过串口或USB接口连接,因此 一 般
无需特别的驱动。
(2)硬件抽象层: GPS硬件模块
a hardware/lib hardware_legacy/include/hardware/gps.h 。
GPS系统是Android中标准的硬件模块,实现后将生成名称为 g p s.<platform>.so的动
态库,放置在目标文件系统/system/lib/hw/目录中。
(3) JNI层:系统服务器定位服务所调用的 一 个本地部分的实现,
a frameworks/base/service /jni/com_android_ server—location_GpsLocationProvider.cpp。
(4) Java框架层:定位的内容包括几个部分
定位部分的Java类比较多,GPS功能是其中主要硬件相关的部分。
a frameworks/base/location/java/android/location/。
android. location为定位的对外包,其中包括定位部分的API和描述接口的aidl,
com. android.internal. location为定位系统内部包。
a frameworks/base/location/java/com/android/internal/location/ 。
a frameworks/base/services/java/com/android/server/LocationManagerService.java。
a frameworks/base/services/java/com/android/server/Iocation/ 。
系统服务器的部分包括com.android.server.location和LocationManagerService, 定位系
统核心的服务为LocationManagerService, 而com.android.server.location是系统服务器中定
位部分的子包。
(5) Java的API和应用层
android. location包中的LocationManager类是定位部分主要的API。这些应用层的内容

�258
第15章定位系统 ooe

可以在普通应用程序中使用,在 Set t i n g s 应用 中也实现 了定位 系统 的设置功能 。


值得注意的是,定位 ( L o c at i o n ) 本身指 的是一 种功能 ,G P S 则是定位 的一 种实现方
式。而实际上, G PS 本 身只是硬件 中的一 种 ,与之相似 的是欧洲 的伽利略系统和 中国的北
斗系统。而 A G PS 表示辅助 的定位 的手段 ,其使用 的硬件和 G PS 完全不 同。由于 G PS 在
定位中的广泛使用,因此定位和 G PS 两个词有 时混用 。

�15.2.2 JNI部分
GPS 的 JN I 部分并没有 复杂的实现 ,大 多数 的 JN I 实现 的 内容仅仅是对 G P S 硬件抽象
层接口部分的直接调用封装。 G PS 的 JN I 部分也是 G PS 硬 件 抽 象层 的唯 一 调 用 者 ,
c o m _ a n d r o i d_ s e r v e r _ l o c a t i o n _G p s L o c a t i o n P r o v i d e r . c p p 文 件是 c o m .an d r o i d .ser v er .l o c at i o n 包
中的 G p sL o c at i o n P r o v i d er 类 的本地部分 。
g e t _ g p s_ i n t e r f a c e O 函数用于后面获得底层 的 G PS 统 一 的操作接 口 G p sl n t er f ac e , 也可以
完成 G PS 硬件模块 的初始化 。可 以在 Jav a 的类 中进行调用 的初始化 函数 的实现如下所 示 :
s t a t i c void android_location_GpsLocationProvider_class_init_native(
JNIEnv* env, j c l a s s c l a z z ) {
method_reportLocation
= env->GetMethodID(clazz, " r e p o r t L o c a t i o n " , "(IDDDFFFJJV");
method_reportStatus = env->GetMethodID(clazz, "reports七atus", " ( I ) V " ) ;
m e t h o d _ r e p o r t S v S t a t u s = e n v - > G e t M e t h o d I D ( c l a z z , " r e p o r t S v S t a t u s " , " {) V " ) ;
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", " ( I I J V " ) ;
method r e p o r t N m e a = e n v - > G e t M e t h o d I D ( c l a z z , " r e p o r t N m e a " , " ( J ) V " ) ;
//省略部分内容

此处得到的 Jav a 类 中的 r ep o rt L o c at i o n 几个方法 ,就是从本地层到 Jav a 层 的调用机制 ,


用于送上定位的数据、状态等信息。
由于 GPS 是 一 个输入型的设备 ,G P S 数据信息需要从本地层传 入 Jav a 层 ,这种信 息
的传递使用的是本地层到 Jav a 层 的回调机制 。l o c at i o n_ c a l l b a c k O 用于获知位置信息回调 函

数,内容如下所示:
s t a t i c v o i d location_callback(GpsLoca七ion* l o c a t i o n ) {
JNIEnv* env = A n d r o i d R u n t i m e : : g e t J N I E n v ( ) ;
env->CallVoidMethod(mCallbacksObj, method_reportLocation, l o c a t i o n - > f l a g s ,
(jdouble) l o c a t i o n - > l a t i t u d e , (jdouble) location->longitude,
(jdouble)location->altitude,
( j f l o a t ) location->speed, ( j f l o a t ) location->bearing,
(jfloa七) l o c a t i o n - > a c c u r a c y , ( j l o n g ) l o c a t i o n - > t i m e s t a m p ) ;
checkAndClearExceptionFromCallback(env, FUNCTION);

l o c a t i o n _c a l l b a c k 经过封装后 ,将 被设置到硬件抽象层 中,由硬件抽象层调用 。在本 函


数中,将反向调用 Jav a 接 口,将数据和消息传 向 Jav a 层 。此处调用 的 m et h o d_ r e p o r t L o c a t i o n
就是 G p sL o c at i o n P r o v i d er 类 中的 r ep o r t L o c at i o n 方法 ,传递 的各个参数是经度 、纬度 、速度
等定位信息。
s t a t u s _ c a l l b a c k ( ) 、sv _ s t a t u s _ c a l l b a c k ( ) 、nm ea_ c a l l b a c k ( ) 等几个 函数 的实现 与之类似 ,均

调用了 Jav a 的方法 ,并且注册到其 中当做 回调函数使用 。

259�
• O o Android板级支持与硬件相关子系统

�15.2.3 Java部分
定位的Java部分包括Java框架层的API和内部的包。从运行的角度,名为"location"
的服务运行于系统服务器进程,通过JNI调用硬件抽象层,框架库中的各个类则运行于其
调用者的进程。

1. GpslocationProvider类
com.android.server.location包中的GpsLocationProvider表示定位的提供者,封装GPS相
关定位数据,以及控制逻辑等,也负责和GPS的JNI部分直接通信。也包括AGPS相关的
使用逻辑。
ReportLocation、reportStatus等方法完成了从本地到Java层的传输传递,其中的 一 个核
心内容如下所示:
p r i v a t e f i n a l I L o c a t i o n M a n a g e r rnLoca七ionManager;
p r i v a t e L o c a t i o n r n L o c a t i o n = new L o c a t i o n ( L o c a t i o n M a n a g e r . G P S _ P R O V I D E R ) ;
p r i v a t e v o i d r e p o r t L o c a t i o n ( i n t f l a g s , double l a t i t u d e , double longitude,
d o u b l e a l t i t u d e , f l o a t speed,
f l o a t b e a r i n g , f l o a t accuracy, l o n g timestamp) {
synchronized (mLocation) {
mLocationFlags = f l a g s ;
i f ( ( f l a g s & LOCATION HAS LAT LONG) == LOCATION HAS LAT LONG) {
mLocation.setLatitude(la已tude); II 得到经度 、纬度 、时间信息 ,并记录
mLocation.setLongitude(longitude);
mLocation.setTime(七imestamp);

if ( ( f l a g s & LOCATION_HAS_ALTITUDE} == LOCATION HAS ALTITUDE} {


mLocation. s e t A lt i tude ( a l t i t u d e } ; / /得到高度信息,并记录
I e l s e { mLoca七ion. r e m o v e A l t i t u d e (} ; )
if ( ( f l a g s & LOCATION HAS SPEED} = = LOCATION HAS SPEED} {
mLocation.setSpeed(speed};
I e l s e { mLocation.rernoveSpeed(};
II 省略 部分 内容
try {
mLocationManager.reportLocation(mLocation, false};
) c a t c h (RemoteException e} { / / 省 略 }

II 省略部分 内容

所调用的ILocationManager实际上是Location定位部分的远程接口,而Location是用
于表示定位信息的类,它们都是android. location包中的内容。
GpsLocationProvider不仅调用GSP定位的硬件抽象层,并且通过打开gps.conf配置文
件,gps.conf放置于目标系统的/etc/目录中,一般的格式如下所示:
NTP_SERVER=north-america.pool.ntp.org
XTRA_SERVER_l=h七tp://xtral.gpsonextra.ne七/xtra.bin
XTRA_SERVER_2=http://xtra2.gpsonextra.net/xtra.bin
XTRA_SERVER_3=http://xtra3.gpsonextra.net/xtra.bin
SUPL_HOST=supl.google.com
SUPL PORT=7276

gps.conf提供 一 些参数作为辅助 的定位手段 ,其 中涉及 的几个相关概念如下所示 。

260
第15章定位系统 ooe

a N T P (Network Time Protocol)用来让计算机之间实现时间同步的协议。



e XTRA是 种 G P S 增强功能 ,用千在没有搜到卫星之前 ,先利用 网络下载星历数据 ,
然后通过这些数据很快找到可使用的卫星,从而提高搜星速度。
e S U P L CSecure User Plane Location, 安全用户面定位),是 一 种基于 IP 的辅助 G PS
定位的手段,基于网络实现快速定位。

2. android.location包
android.location包是Android系统定位部分主要的包,其中既包括平台层的APL也包
括部分隐藏(@hide)类和接口,仅供内部使用。
android.location包作为API使用的类如下所示。
e LocationManager: 定位系统的入口类。
e Location: 表示定位信息的类。
e LocationProvider: 抽象类,用于发送定期的地理信息。
e LocationListener: 用千监听位置的变化。
e LocationListener: 用于监听GPS状态的变化。
LocationManager是定位系统的入口类,其中具有如下方法:
p u b l i c L o c a t i o n 釭 o v i d e r g e t P r o v i d e 工 ( S t r i n g name) / / 获 得 Lo c a t i o n Pr ov i d e r
p u b l i c Location getLastKnownLocation(String provider) / / 获 得 最 后 一 个位置
p u b l i c Lis七<String> getProviders(boolean enabledOnly) / / 获 得 使 能 的 L o c a t i o n Pr ov i d e r
II捡查_L立ca臣onProvider


getLastKnownLocationO方法用于直接得到最后 个定位信 息 ;getLastKnownLocation()

用千获得 个 L ocationProvider。

提示:com.android.internal.location包中的DummyLocationProvider是LocationProvider
另外的一种实现者,也是被LocationManager所调用的。

LocationManager中设置监听相关的方法如下所示:
p u b l i c v o i d requestLocationUpdates ( S t r i n g p r o v i d e r , l o n g minTime,
f l o a t minDistance, LocationListener l i s t e n e r )
p u b l i c v o i d requestLocationUpdates ( S t r i n g p r o v i d e r , l o n g minTime,
f l o a t m i n D i s t a n c e , L o c a t i o n L i s t e n e r l i s t e n e r , L o o p e r loope-r)
p u b l i c v o i d removeUpdates ( L o c a t i o n L i s t e n e r l i s t e n e r )
p u b l i c boolean addGpsStatusListener(GpsStatus.Listener l i s t e n e r )
public void removeGpsStatusListener(GpsStatus.Listener.listener)
p u b l i c v o i d r e q u e s t L o c a t i o n U p d a t e s ( S t r i n g p r o v i d e r , l o n g minTime,
l o a t m i n D i s t a n c e 1 .f.空ridi_n.9,In_tent _i_gtent_)

req uestLoca tion Updates()和remove Updates()方法分别用千设置和移除定位信息的监听


器(LocationListener), addGpsStatusListener()和removeGpsStatusListenerO方法分别用于设
置和移除GPS状态信息的监听器(GpsStatus心stener), requestLocationUpdates()用于通过
Pendinglntent处理对位置更新的相关内容。
LocationManager通过LocationListener将位置的变化信息传递到用户程序。定义在
LocationListener.java文件中,内容如下所示:

261
•oO Android板级支持与硬件相关子系统

public interface LocationListener {


void onLocationChanged(Location location); II位置变化时触发
void onStatusChanged(String provider, //状态变化时触发
int status, Bundle extras);
void onProviderEnabled (String provider); // GPS开启时触发
VO过 onProv ide rDisab led (String provider); // GPS关闭时触发

android.location包作为内部使用aid! 接 口 如 下 所 示 。
。 IL o catio n M an ag er: 定 位 管 理 器 的 接 口 , 由 L o c a t i o n M a n a g e r S e r v i c e 实 现 。
a ILocationListener: 定 位 信 息 的 接 口 , 由 L o c a t i o n M a n a g e r S e r v i c e 实 现 。
a IGpsStatusProvider: G P S 状 态 获 取 接 口 , 由 G p s L o c a t i o n P r o v i d e r 实 现 。
a IGpsStatusListener: G P S 状 态 获 取 接 口 , 由 L o c a t i o n M a n a g e r 实 现 。
ILocationManager接口的主体内容如下所示:
interface ILocationManager

List<String> getAllProviders{);
List<String> ge 七Prov ide rs {in Criteria criteria, boolean enabledOnly);
String getBestProvider{in Criteria criteria, boolean enabledOnly);
boolean providerMeetsCriteria{String provider, in Criteria criteria);
void requestLocationUpdates{String provider, in Criteria criteria, long minTime,
float minDistance, boolean singleShot, in ILocationLis 七ene r lis 七ene r );
II省略部分内容

ILocationManager是联系上下层的接口,其实现者LocationManagerService运行于系
统服务器(SystemServer)进程。LocationManager是其主要调用者,部分代码片段如下
所示:
private ILocationManager mService; //保存让QC云5石Manager的句柄
public List<String> getAllProviders () { //作为API的函数
try {
return mService.getAllProviders(); II调用ILocationManager的实现
) catch (RemoteException ex) { //省略 }
return null;

LocationManager的getAllProviders()函数是通过调用ILocationManager中的同名函数完
成的,requestLocationUpdates()、addGpsStatusListener()等主要方法的实现也与之类似。

3. L o c a t i o n M a n a g e r S e r v i c e 包

LocationManagerService是ILocationManager接口的实现者。它是定位系统的公共部
分,运行于系统服务器的进程中,在SystemServer中通过ServiceManager将其注册为名为
"location"的服务。

LocationManagerService本身也是Runnable的实现者,作为 个线程运行 。
LocationProviderlnterface是com.android.server.location包中的接口,表示可以获取定位
信息提供者。GpsLocationProvider和LocationProviderProxy都是LocationProviderlnterface的
实现者。LocationManagerService将要和LocationProviderlnterface进行交互,其中保存的句

�262
第15章定位系统 ooe

柄如下所示:
private final ArrayList<LocationProviderin七erface> m P r o v i d e r s =
n e w A r r a y L i s t < L o c a t i o n P r o v i d e r i n t e r f a c e > ();
p r i v a t e final HashMap<String, L o c a t i o n P r o v i d e r i n t e r f a c e > m P r o v i d e r s B y N a m e
= n e w HashMap<String, L o c a t i o n P r o v i d e r i n t e r f a c e > O;

LocationManagerService类中的mProviders和rnProvidersByName的成员就来自系统中
可以提供LocationProviderlnterface的实现。

reportLocationO是其中的 个方法 ,通过下面的调用完成 :
p u b l i c v o i d reportLocation(Loca七ion location, b o o l e a n passive) {
II省略部分内容
m L o c a t i o n H a n d l e r . r e m o v e M e s s a g e s ( M E S S A G E L O C A T I O N CHANGED, location);
M e s s a g e m = M e s s a g e . o b t a i n ( m L o c a 七io n H an d le r ,
M E S S A G E L O C A T I O N CHANGED, location);
m . a r g l = ( p a s s i v e ? 1 : 0);
m L o c a t i o n H a n d l e r . s e n d M e s s a g e A t F r o n t O f Q u e u e ( m ) ; //进行消息汇报

此处进行的是对定位信息的直接设置,而不是来自底层的汇报。mLocationHandler类

型是 个 H andler 的继承者 ,其 中将调用 L ocationProviderlnterface 进行 消 息 的汇报 。

15.3 定位BSP的结构

GPS定位系统移植的主要内容是驱动层和硬件抽象层。GPS硬件设备的驱动程序通常
是串口驱动程序,在Linux系统的用户空间表示为TTY设备。
GPS的硬件抽象层实现比较复杂,也是Android系统中使用GPS和其他系统的主要区
别所在。Android给出了简单的参考实现 gp s_qem u.c, 基本结构可以重新使用,但是需要在
此基础上进行修改,完善具体的功能并适配具体的硬件。

汕 15.3.1 驱动程序

GPS 般分为软和硬两种 。前者直接输 出卫星数据 ,需要应用处理器端进行解析计算 ,
再 转 换 成 标 准 的 N M E A (National Marine Electronics Association, 国际海洋电子协会)数据。
而后者直接在片内进行解析,直接输出NMEA数据。本章主要讨论硬GPS, 以下提及GPS,
如无特殊说明,均指硬GPS。

GPS的硬件接口相对简单,除了供电、reset等控制, 般仅通过 串 口和应用处理器连
接,因此驱动程序通常是标准的串口驱动。部分GPS只需上电,即可开始找到卫星,并发
回携带信息的NMEA数据。找到足够的卫星之后,导航即可开始。也有 一 部分 G P S 上 电
后,允许用户写入命令,配置信息(包括星历等信息加速定位速度),乃至固件。对于这类
GPS, 需要根据其要求,在硬件抽象层中实现对应的操作。
将GPS连接上应用处理器的某个串口之后,通常表现为设备节点/dev/ttySx, x表示其
对应编号。可以打开该节点进行写操作,对GPS写入数据或命令。而读操作获取到的,
般就是标准的NMEA数据。

263�
• O o Android板级支持与硬件相关子系统

夕 提 示 : 某 些 系 统 的 G P S 实 现 比 较 特 殊 , 例 如 高 通 7 2 2 7 平 台 的 G P S , 集成在基带处
理器端,因此驱动相对复杂,是在kernel中实现的基 于Share Memory的RPC等机制。

15.3.2 硬件抽象层的接口
GPS硬件抽象层的头文件为gps.h, 其核心的内容为gps _device_ t, 是 h w _device_ t结构
体的扩展,如下所示:
s t r u c t gps_device_t {
s t r u c t hw d e v i c e t common;
const Gpsinterface* (*get_gps_interface) (struct gps_device_t* dev);
);

其中定义的函数指针返回类型为Gpslnterface, 是GPS硬件抽象层的主要接口。

提示: Android 2.2及之前版本中,GPS不是一个硬件模块,其接口是Gpslnterface,


在Android 2.3及之后的版本中,接口的形式改成了硬件模块,但是Gpslnterface依然作
为接口,与从前基本相同,但通过gps_device—t进行了封装。

关键接口Gpslnterface的内容如下所示:
typedef struct { //定义标准的 GPS· 接口的结构体
int { * i n i t ) { GpsCallbacks* c a l l b a c k s ) ; / / 初 始 化 GPS, 提供回调函数实现
int (*start) (void) ; I I 开始导航
int {*stop) ( v o i d ) ; , I I 停止导航
void {*cleanup) ( v o i d ) ; I I 关 闭接 口
int ( * i n j e c t _ t i m e ) (GpsUtcTime t i m e , I I 登入 当前 的时间
i n t 6 4 _ t timeReference, i n t u n c e r t a i n t y ) ;
int (*inject_loca巨on) (double l a 巨 t u d e , I I 登入 当前 的位贤
double longitude, floa七accuracy);
void (*delete_aiding_data) {Gps沁dingData f l a g s ) ; I I 删 除 帮助信 息
int ( * s e t _ p o s i 豆 o n _ m o d e ) ( G p s P o s i t i o n M o d e mode, I I 设置位置模式
int fix_frequency);
c o n s t v o i d * { * g e t e x t e n s i o n ) ( c o n s t c h a r * name); //获得扩展信息的指针
) Gpsinterfpce;

GpsCallbacks定义GPS回调函数指针组,定义如下所示:
I I 位 置信 息的回调函数
t y p e d e f v o i d (*gps_location_callback) (GpsLocation* l o c a t i o n ) ; //位贺信息回调
七ypedef v o i d ( * g p s _ s t a t u s _ c a l l b a c k ) (GpsSta七us* s 七 a t u s ) ; //状态信息回调
t y p e d e f v o i d ( * g p s _ s v _ s t a t u s _ c a l l b a c k ) (GpsSvStatus* s v _ i n f o ) ; //卫星状态信息回调
t y p e d e f v o i d ( * g p s _ n m e a _ c a l l b a c k ) (GpsUtcTime t i m e s t a m p , c o n s t c h a r * nmea,
i n t leng七h); / / 直 接 上 传 NMEA 数据 回调
typedef struct { / / GPS 回调函数结构体
gps_location_callback location_cb;
gps_status_callback status_cb;
gps_sv_status_callback sv_status_cb;
g p s _ n m e a _ c a l l b a c k nmea_cb;
) GpsCal_lbacks;

回调函数是GPS硬件抽象层的调用者获得信息的手段 ,通常在读取到底层数据并分析
完成时调用,上报信息给上层。GpsCallbacks通过调用初始化函数(Gpslnterface中的init)

�264
第15章定位系统 ooe

注册到硬件适配层,供适配层在适当时回调。
GpsCallbacks中的location_c b 是 接 收 到 位 置 相 关 信 息 时 的 回 调 函 数 。 其 中 的 参 数
GpsLocation代表GPS的定位信息(经纬度、速度、方向、时间等),内容如下所示:
typedef struct {
uint16_t flags; //标志位
double latitude; //纬度(以度为单位)
double longitude; //经度(以度为单位)
double altitude; //以WGS 84 (World Geodetic System 1984)坐标表示的高度信息
floa七speed; //速度(以m/s为单位).
float bearing; II方向(以度为单位)
float accuracy; //精确度(以m为单位)
GpsUtcTime timestamp; II时间戳
L Gps"Location;

GpsLocation中的成员表示为解析出来的详细定位信息,而非GPS原始数据。
GpsCallbacks中的status_cb是状态的回调函数,GPS的状态信息由GpsStatus结构表
不,在状态回调中使用,如下所示:
typedef struct {
GpsStatusValue status; //状态信息

状 态 信 息 在 G P S 状 态 更 新 时 , 通 过 g p s_status_ callback类型回调函数通知上层。
GpsCallbacks中的sv_status_cb和nmea_cb, 分 别 表 示 卫 星 状 态 和 N M E A 信 息 , 使 用
了表示卫星相关信息GpsSvlnfo等结构体。
gps.h中的其他内容提供了对定位的辅助支持。其中,XTRA用于从网络下载星历,加
速找星速度。XTRA相关接口定义如下所示:
typedef struct [ / / 扩 展 X T R A 的 支 持
int (*init) (GpsXtraCallbacks* c a l l b a c k s ) ; // XTRA的初始化
int (*inject_xtra_data) ( c h a r * data, i n 七 l e n g t h ) ; //注入XTRA数据
} QpsXtrainterface;

AGPS是使用手机基站的定位方式。gps.h中AGPS相关的接口定义如下所示:
typedef struct { / / 扩 展 A G P S 的 支 持
void (*init) (AGpsCallbacks* c a l l b a c k s ) ; //初始化,注册回调函数
int (*data_conn_open) ( c o n s t char* a p n ) ; //通知数据连接关闭
i n 七 ( * d a t a conn closed) () ; //通知数据连接关闭
int (*data conn failed) (); //通知数据连接失败
int (*set_server) ( A G p s T y p e type, II设置访问AGPS服务器的相关配置
const char* hostname, i n 七 p o r t ) ;
) AGps Ink-e_rf ace;

AGPS系统的回调函数由AgpsCallbacks表示,相关内容如下所示:
// AGPS状态的信息
typedef void (* agps_status_callback) (AGpsStatus* status);
七ypedef struct I
agps_status_callback status_cb; // AGPS状态的信息
) AS,psCal_J.backs;

回调中使用的参数类型为AgpsStatus, 表示获取AGPS的状态信息,定义如下所示:

265�
•oo Android板级支持与硬件相关子系统

typedef struct {
AGpsType type;
AGpsSta七usValue s t a t u s ;
} AGpsStatus;

Android的GPS的硬件抽象层还包含network initiated部分代码(Qualcomm贡献的),
在同目录的 gp s_ni.h 文件 中定义 。

迪15.3.3 实现硬件抽象层
” ”
对于GPS, 首先需要完成初始化,然后对于GPS数据,建立 一 套 获取— 解析— 上报
的机制。
首先在Gpslnterface的init函数指针的实现中,完成对GPS的初始化。初始化过程在
GPS被打开时调用。该步骤需要完成GPS内核模块的加载工作,部分GPS还需要进行
fmnware下载工作。之后打开GPS端口,确认GPS硬件已经正常工作。
NMEA数据的得到包括了几个部分,首先从来源中获取到数据,接下来对数据进行解
析,然后向各个层次传递。
GPS初始化完成后,一般都会新开 一 个线程 ,
对 G PS 端 口进行轮询 ,
获取输 出的 N M EA
数据。Gpslnterface的start和stop主要控制该线程的启停,在实际的操作过程中需要考虑
到省电的处理。
解析的工作在获取数据后进行,这里的目的是将NMEA数据解析成Android框架可以
识别的结构信息,存放到GpsLocation及GpsSvlnfo等,以便上报。对于NMEA数据的解
析,已经有非常多的参考实现。
然后进行数据汇报过程,汇报过程在解析完后进行,通过所调用的回调函数并填入获
取到的数据结构完成。
XTRA和AGPS的适配比较简单,无论XTRA还是AGPS, 芯片商都已经提供好对应

的库或直接硬件实现。因此适配方面 般没有 复杂的操作 ,只需要实现相应 的接 口。X T R A
要实现将XTRA数据导入GPS的操作,AGPS主要完成对服务器的相关配置,以及对data
connection的启停响应。

15.4 定位BSP的实现

�15.4.1 仿真器的GPS实现
Android包含仿真器中包括GPS部分的硬件抽象层,用于仿真器的goldfish平台。
在Android 2.3中代码路径为: sdk/emulator/gps/。
在Android 4.x中代码路径为: development/too Is/emulator/ system/ g p s/。
将生成动态库 gp s.goldfish.so , 放置于/system/lib/hw目录中。

其中只有 个源 代码 文件 gps_qem u.c, 基 于 模 拟 器 环 境 的 G P S 适 配 层 。 由 于 N M E A
数据的解析等是比较统 一 的,在编写特 定 G PS 适配层 时,以 g p s_qem u.c 中大部分的处理流

�266
第15章定位系统 oo•

程作为基础进行改写。
在仿真器的实现中没有针对某个GPS硬件的源码,实现的部分分成接口实现、GPS相
关控制,以及NMEA解析部分。其中NMEA解析部分比较独立,提供解析功能接口和回
调接口。
qemuGpsinterface是Gpslnterface类型的结构实现,如下所示:
s t a t i c const Gpsinterface qemuGpsinterface = {
qemu g p s i n i t , //初始化 GPS, 提供回调函数实现
qemu g p s s t a r t , //开始导航
qemu g p s s t o p , //停止导航
qemu g p s c l e a n u p , //关闭接口
qemu_gps_inject_time, //置入当前的时间
qemu_gps_inject_loca已on, II置入当前的位置
qemu g p s d e l e t e a i d i n g d a t a , //删除帮助信息
qemu_gps_set_position_mode, II设置位置模式
qemu_gps_get_extension, //获得扩展信息的指针

其中的初始化过程qemu _gps_ init函数调用gps_state_ init实现,主要内容如下所示:


static void gps_state init (GpsState* state) {
state->1.n1.t = l;
state->control[OJ = -1;
state->control[l) = -1;
state->fd = -1;
s t a t e 今 f d = qemud c h a n n e l open(QEMU CHANNEL NAME); / / 打 开 Q E M U 通道
II省略部分内容
i f ( s o c k e t p a i r ( A F _ L O C A L , SOCK_STREAM, 0 , s t a t e - > c o n t r o l ) < O ) { g o t o F a i l ; )
i f ( p t h r e a d _ c r e a t e ( & s t a t e - > t h r e a d , N U L L , g p s _ s t a t e _ t h r e a d , s t a t e ) !=O) { g o t o F a i l ; )
return;
Fail:
gps_sta七e_done(s七ate);

Q E M U _ C H A N N E L _ N A M E 被 定 义 成 " g p s ", 这是 一 个用千模拟 G P S 功能 Socket, 用


于模拟GPS、NMEA数据生成。
gps_state_thread是核心的数据处理线程。它完成的处理流程为:获取一解析—上报。
获取部分,gps_state_thread通过epoll_wait从socket判断是否有数据到达。随后通过read
函数直接读取。并将读取的数据送到runea_reader_addc进行解析。
解析过程是非常有特点的字符串解析方法。NMEA有非常明确的域分隔符和开始结束
符。因此 gp s_qem u 实现 了一 套基于 T O K E N 的简单解析方法 ,并完成对 G psL ocation 等结
构的填写。
N M E A 数 据 解 析 的 主 函 数 是 r u n e a_reader_parse, 定义如下所示:
static void_nmea_reader_parse(NmeaReader* r)

N m e a R e a d e r 是 用 千 表 示 N M E A 数 据 的 一 个 结 构 体 ,包 括 pos 和 utc 等 成 员 。
nrnea_reader_parse函数中通过rnnea_ tokenizer_get获取每个域的数据,并根据格式定义转换
成对应的格式,再通过runea_reader_update_xxx函数Cxxx为time、altitude等)更新对应
的域,比如时间、经纬度等。这是解析的流程。

267�
•oO Android板级支持与硬件相关子系统

在上报方面,由NMEA解析部分的回调函数完成。在其初始的阶段注册的
callbacks.location_cb, 在运行中对其进行调用,完成位置信息的上报。
gps_state—thread的启动和停止的控制实现比较简单,因为正好要从socket中的
epoll_wait读取数据,因此启停控制也通过epoll完成,轮询某个控制文件句柄。函数
qemu _gps_ start和qemu_gps_ stop操作这个控制句柄。gps_state_thread中,则通过设置和取
消NMEA解析的回调函数,来打通或关闭回调并上报GPS数据消息的途径。
gps_qemu并没有GPS系统实现nsion中的任何 一 种辅助定位方法 。

汕 15.4.2 Nexus One 系统 的实现


高通平台的GPS硬件,通常集成在基带处理器 一 侧 ,与应用处理器 的通信 M odem 部
分 一 样 ,同样基于 Share M e m o r y 上 的 高 通 特 有 R P C (远程过程调用)协议实现。因此高通

的GPS硬件抽象层分为两部分: 部分为硬件抽象层接 口的实现 ;另一 部分为基于 R PC
协议封装的具体控制和数据操作,该部分代码主要通过高通的RPC代码生成器生成。
Nexus O n e 系 统 使 用 了 A n d r o i d 开 源 代 码 中 的 高 通 M S M 平 台 的 G P S 。 在 A n d r o i d 的 开
源工程中,其代码路径为: hardware/qcom/gps/loc_api, 生成动态库gps.mahimahi.so。
高通GPS硬件抽象层的实现部分,包括以下几个部分。
• gps.c: 硬件抽象层的接口部分。
• loc_eng.*: 主要的实现部分。
• loc_eng_ioctl户:用千承载到glue层的具体控制和回调。
• loc_eng_ni.*: 网络部分的初始化。
• Ioc_eng—
xtra.*: XTRA模块的实现。
g p s . c 是 G P S 模 块 的 入 口 , 定 义 了 G P S 的 h w_module_t 和 h w_module_methods—
t结构 ,
打开函数open_gps(), 通过调用get_gps_ interface()返回Gpslnterface类型的结构,作为硬件
抽象层的导出C语言接口。
loc_ eng.cpp中定义Gpslnterface结构如下所示:
static const Gpslnterface sLocEnginterface = {
sizeof(Gpsin七erface),
loc eng init, //初始化GPS, 提供回调函数实现
loc eng start, //开始导航
loc_eng_stop, //停止导航
loc_eng_cleanup, //关闭接口
loc eng inject time, II置入当前的时间

---
loc eng , inject

location, II登入当前的位控
loc eng delete aiding data, //删除帮助信息
loc eng set position mode, //设置位咒模式
loc eng get extension, II获得扩展信息的指针
.'


返回Gpslnterface类型接口的实现。高通版本的GPS硬件抽象层包含GPS数据的 获
” “ ”
取 和 解析 过程 ,它们都通过 R PC 上报 ,在生成 的 R PC 相关代码 中完成 。然后再调
用loc—
eng—
init 注册 的回调 ,向上层上报 。
上报部分的代码的实现在基于loc_ eng_process_deferred_ actionO函数的线程中。该线程

�268
第15章定位系统 ooe

实现 一 个等待循环 ,当有 G PS 数据上报并解析完毕 以后 ,R PC 部分会 回调 loc_eng_血 t 时


注册的loc—event_cb, 该回调函数会激活线程中的等待循环。
线程通过loc_ eng_process_Joe_event函数处理获取的数据,之前已经解析完毕,因此该
函数直接调用loc_ eng_report_ xxx (例如位置position, 卫星状态sv等),将数据通过上层
注册的回调函数,进行上报。这部分代码如下所示:
s t a 豆 c v o i d lo c _ e n g _ p r o c e s s _ l o c _ e v e n t (rpc_loc_event_mask_type loc_event,
rpc_loc_event_payload_u_type* loc_event_payload) {
if (lee event & R P C LOC E V E N T P A R S E D POSITION REPORT) { / / 位 置 信 息
loc_eng_repor七_position (&(loc_event_payload->
rpc_loc_event_payload_u_type_u.parsed_location_report));

if (loc_even七& RPC_LOC_EVENT_SATELLITE_REPORT) { //卫星信息


l o c _ e n g _ r e p o r t _ s v (&(loc_even七_payload->
rpc_loc_event_payload_u_type_u.gnss_report));

if (loc_event & RPC_LOC_EVENT_STATUS_REPORT){ //状态信息


loc_eng_report_status (&(loc_even七_payload
->rpc_loc_event_payload_u_type_u.status_report));

if (loc event & R P C LOC E V E N T N M E A POSITION REPORT) { / / N M E A 信息


loc_eng_r e p o r t _ n m e a (&(loc_event_payload
今 rp c_ lo c_ e v e n t主 a y lo a d_ u_ 七yp e_ u .nm e a_ rep o rt )) ;

//省略部分内容


高通GPS硬件抽象层虽然步骤繁多,但原理与 gp s_qem u 致 ,只是上报 的信息完善
很多,包含比如卫星数据、NMEA数据直接上报等。
高 通 G P S 还 实 现 了 X T R A 与 A G P S , 其中AGPS的接口在sLocEngAGpslnterface中实
现,内容如下所示:
static const A G p s i n t e r f a c e s L o c E n g A G p s i n t e r f a c e = {
loc_eng_agps_init, //初始化,注册回调函数
loc_eng_agps_data_conn_open, //通知数据连接关闭
loc_eng_agps_data_conn_closed, I I 通知数据连接 关闭
loc_eng_agps_da垣_conn_failed, I I 通知数据连接 失败
loc_eng_agps_set_server, I I 设置访 问 AG PS 服 务器的相关配置
);

Joe_eng_ag p s_ set_ s e r v e r 函 数 完 成 对 S e r v e r 的 配 置 。 硬 件 抽 象 层 根 据 l o c_ eng_ set_


position_mode中传入的配置,决定是否启用AGPS。启动函数为set_agps_ server。该函数最
终 通 过 R P C 将 命 令 写 入 G P S , 其代码片段如下所示:
ret_val = loc_eng_ioc七l (loc_eng_data.client_handle,
RPC LOC IOCTL SET UMTS S L P S E R V E R ADDR, &ioctl data,
LOC_IOCTL_DEFAU竺_TIMEOUT, N U L L //没有输出信息

调用的Joe_eng_ioctl()函数参数中的ioctl_data包含了所有的服务器的信息。

迪 15.4.3 Nexus S 系统 的实现


Nexus S系统的GPS硬件抽象层部分以二进制的形式发布,在目标系统中,包括以下

269�
• 0 0 Android板级支持与硬件相关子系统

的两个部分。
e /systern/vendor/lib/hw/gps.s5pcl10.so: 硬件抽象层库。
e /systern/vendor/etc/gps.xml: GPS的配置文件。
gps.xml文件是GPS的配置文件,需要配合硬件抽象层使用,提供 一 些可配置内容的
值,其中的片段如下所示:
<hal acPor七Name = "/d ev / s3c2 4 10_ se r ia l l " 1BaudRate = "1 1 52 00 " c L o g E n a b l e d = "fa l se "
cLogEnab l e i n i t S t a t e = " fa l se " acLogDirec七ory="/data/gps/log/"
ltoFileName = "lto 2 .d a t " e n h a n c e d - a s s i s t e d = " fa lse " c p - e n h a n c e d - a s s i s t e d = "t ru e "
T I S E n a b l e d = "fa l se " RILEnabled="true" LPmode = "fa lse "
cp-cold-s七art = "fa lse " cp-guard-time-sec = "2 " arp-supl-enable = "t ru e "
arp-supl-cap-msb="true" arp-supl-cap-msa = "t rue " arp-supl-cap-ecid="false"
a c S u p l S e r v e r = "sup l .go o g le .com " Sup1Port = "7 27 6 " LbsEnable = "t ru e "
LbsLocal = " fa l se " L b s S e r v e r = "b cm l s 2 .g lp a ls .c om "
LbsPort = "72 75 " L b s S y n c L t o = "t ru e " LbsSyncCells = "tru e " LbsWlanEnable="false"
L b s S y n c L t o T h r e s h o l d D a y s = "3 "
gpioNStdbyPath="/sys/class/sec/gps/GPS_PWR_EN/value"
gpioNResetPath="/sys/class/sec/gps/GPS_nRST/value"
gpioDelayMs = "2 50 " lcsApiSockName = " /dev /so c ke 七/g p s "
acNvStoDir = " /d a t a /g-es / " />

gps.xml中包括GPS的设备节点信息等参数,其中的/dev/s3c241 O_seriall就是GPS的
设备节点。

汕 15.4.4 Galaxy Nexus 系统 的实 现


由于GPS的硬件对于处理器来说连接的只是 一 个串口。 G alaxy Nexus系统的GPS部分
使用OMAP处理器的通用实现,包括以下儿个文件。
e /system/vendor/lib/hw/gps.omap4.so: OMAP4硬件抽象层的库。
e /system/etc/gps.conf: GPS的配置文件。
• /system/vendor/etc/s订fgps.conf: GPS的配置文件。
e /system/lib/lib_gsd4t.so: G S D 4 T芯片的专用库。
gps.conf是通用的GPS配置文件,其内容如下所示:
XTRA_SERVER_l = h 七七p :/ /x t ra l .gp s one x t ra .n et /x t ra .b in
XTRA_SERVER_2 = h t tp ://x 七ra 2 .gp so n e x 七ra .n et /x t ra .b in
XTRA_SERVER_3 = h t tp :/ /xtra3. g p s o n e x t r a .net/xtra ,,bin
S U P L _ H O S T = sup l .g oo g le .com
SUPL PORT.,7276

sirfgps.conf是vendor的GPS配置文件,其中设置串口为/dev/ttyOO, 以及相关GPIO
的配置,还有 一 些布尔值的宏。

270
第16章
电话系统

16.1 电话系统概述

Android 系统主要的使用方面是智能 电话 ,因此 电话部分是 A ndro id 的核心子系统之 一 。


Android 的 电话 系统 围绕底层使用 的 M odem 硬件来搭建 。A ndroid 主要提供呼叫 (C alling ) 、
短信息 (S M S ) 等业务 ,此外通过 电话系统还可 以实现数据连接 (D ata Connection), 实现
网络功能。
Android 的软件 系统是 M odem 设备的使用者 。在 L in ux 内核 中包含 了 M odem 的驱动
程序。在用户空间, R IL 库作为本地 的实现部分 ,Java 框架 电话系统对上层 的 Jav a 提供 了
接口。
Android 电话 系统 的相关 内容如表 16-1 所示 。

表16-1 Android 电话系统的相关内容


Android 层次 电话系统部分 描 述

硬件层次 Modem 硬件 、串口等连接端 口

操作系统层 TTY设备 一般使用 AT 命令接 口


硬件抽象层 RLL实现库 实现R1L标准接口

本地层 nld 守护进程 ,libnl库 没有 JN J 部分


Java 框架层 android.telephony和com.anclro1d.intemal.telephony 包 通过 rild socket 连接 nld 守护进程
Java 应用层 Phone 应用 包括 ServiceM anager 中的服务

16.2 电话子系统的结构

�16.2.1 总体结构
Android 的 电话系统本身 比较庞大 ,除 了电话本身的核心功 能之外 ,还涉及 与声音 、定
位等部分的功能,因此在实现上与其他系统有交叉。电话主体部分的结构如图16-1所示。
•oo Android板级支持与硬件相关子系统

Phone应用
Java应用层

android .telephony . •

com.android .internal.telephony.•

RIL Part S亟t"rild"
Java框架层

rild d a e m o n

RIL lnit/ RIL_slartEventLooA)


RIL_RadioFunctions RIL_reglste�)
RIL_onRequestComplet•)
RIL_onUnsollcitedRespons付
:;'.;;'.,'::::'-
RIL_requestTlmedCallbac�)

lib<XXX>-ril.so
本地框架
,.一.一·
----..1

Linux内核层 i Modem D印er ( U A R T 、 S 0 1 0 、 U S B )
移部
棺分

图16-1 电话系统部分的结构

自下而上,Andro闪电话部分的各层内容如下所示。
(I)驱动层
Modem的驱动程序在Linux内核中实现,提供成TTY设备的形式,一般分为AT命
令通道和数据通道这两路接口。
(2) 硬件抽象层 :RIL实现库
RIL实现库调用Modem的驱动程序,并且实现RIL层的接口,被rild守护进程所使用。
(3)本地的RIL层
R且部分主要的代码路径如下所示。
a hardware/ril/include/: ril.h为RIL各层的公共头文件。
a hardware/ril/libril: libril库,作为宫颈使用。
a hardware/ril/rild: RIL守护进程,生成rild可执行程序。
电话部分无JNI部分,RIL守护进程通过名称为rild的套接字与Java框架层进行通信。
(4)电话部分的Java框架层
代码路径: frameworks/base/telephony/java/, 在这个目录中相应的内容如下所示。
a android/telephony: 包含gsm和cdma这两个子目录。
a com/android/internal/telephony: 内部实现类,包含gsm和cdma这两个子目录。
a services/java/com/android/server/TelephonyRegistry.java: 电话注册服务。
android. telephony是Java框架层的API, com.android.internal. telephony及其子包则是电
话部分的内部实现。
(5) Phone应用
a package/app/Phone: 包括实现了框架性质的phone服务,呼叫(Call)、短信(SMS)

272
第16章 电话系统 oo•

等电话的核心服务。

�16.2.2 rild层
RIL 层 由 rild 可执行程序、libril 库和 RIL 实现库三部分组成,其中 RIL 实现库是作为
电话部分的硬件抽象层实现的。 libril 库是一 个工具。rild 使用动态的方式打开 RIL 实现库 ,
并取出其中的符号,获得 一 些函数指针并调用。RIL 层结构如图 16-2 所示。

rild socket

rild守护进程 libril.so

' R J L !nit
:,干 nse

RIL_lnit
i返面
已 �
di oFu nctions
i_

请求返回 主动上报


L_L= lib<XXX>-ril.so

.........•..........
用户空间
------------------------- AT COM邓ND

内核空间 M o d e m 驱动程序

图 16-2 RIL 层结构

名称为 "rild" 的套接字是 rild 守护进程与上层通信的接 口,而动态打开库并进行调用则


是 rild 守护进程与 RIL 实现库的接 口。两个层次接 口的形式不同,但都是 RIL 命令。
rild 守护进程和 RIL 实现库交互流程如下所示。
rild 可执行程序 以守护进程运行,以 rild 作为对上层的接 口。
rild 守护进程 dlopen 动态打开 RIL 实现库,并通过调用 dlsym 取出 RIL_Init 符号 (函
数指针)。
e rild 守护进程中实现 RIL_Env 结构当中的几个函数指针。
rild 守护进程调用 R IL_Init , 并传入 RIL_Env 结构。
e RIL 实现库实现 RIL_Ra 山oFunctions 结构当中的几个函数指针。
e RIL_Init 函数返回 RIL_RadioFunctions 结构,供 rild 守护进程使用,初始化完成。
init.rc默认将rild 定义为守护进程的内容如下所示:
service ril-daemon /system/bin/rild
s o c k e t r i l d s t r e a m 660 r o o t r a d i o
s o c k e t r i l d - d e b u g s t r e a m 660 r a d i o s y s t e m
user root
q工oua r a d i o c a c h e i n e t m i s c a u d i o

273�
•oo Android板级支持与硬件相关子系统

此处的ril-daemon是服务的名称,守护进程以rild为可执行程序,没有附加参数。rild
和rild-debug是Android系统的保留套接字,具有/dev/socket/中的套接字文件。
rild可执行程序可以通过附加参数的形式指定其所使用的实现库、端口信息等。
service ril-daemon /sys_tem/bin/rild -1 /sys_tem/lib/libx-ril,..so -d /dev/ttyUSBO

此处的4参数由rild解析,如果系统中同时存在不同的RIL实现库,可以在这里指定
使用的库名,而-d参数指定了端口的设备。rild运行后会在调用RIL_Init时,将端口传入
RIL的实现库由其进行处理。因此,可以在RIL的实现库自行支持需要的端口类型,也可
以使用默认的端口。

ril.h定义的RIL_Env是RIL层的 个核心结构 ,如下所示 :
struct R I L E n v {
void (*OnReques七Complete} ( R I L _ T o k e n 七 , R I L _ E r r n o e, //请求完成
v o i d *response, size_t responselen};
v o i d (*OnUnsolicitedResponse) (int unsolResponse, II 上报 消息响应
const v o i d *data,size_t datalen);
v o i d (*RequestTimedCallback) (RIL T i m e d C a l l b a c k c a l l b a c k , II 请求中周期性处理
v o i d *param, const struct timeval *relativeTime);
);

该结构可供作为硬件抽象层的RIL实现库调用。用千在发生请求时针对不同情况做出
响应,包含请求完成函数、上报消息响应函数,以及请求中周期性处理函数三个接口。
rild使用dlopen动态打开RIL实现库后,将调用dlsym取出其中的RIL_lnit符号并调
用。函数原型如下所示:
const R I L R a d i o F u n c t i o n s *RIL_II}.!_七(const st王uct R I L _ E n v *env, int argc, char **argv);

rild调用RIL_Init的过程中将完成RIL_Env结构的传递,此结构中的函数指针将被RIL
实现库使用。其函数的返回值类型是RIL _ RadioFunctions类型的结构体指针。

RIL _RadioFunctions结构体由 系列 函数指针组成 ,其相关定义如下所示 :
typedef v o i d (*RIL_RequestFunc) (int request, v o i d *data,
size_t datalen, RIL_Token t);
typedef R I L _ R a d i o S t a t e (*RIL_RadioStateRequest) ();
typedef int (*RIL_Supports) (int requestCode);
typedef v o i d (*RIL_Cancel) (RIL_Token t);
typedef v o i d (*RIL_TimedCallback) (void *param);
七ypedef struct {
int version;
R I L _ R e q u e s t F u n c onRequest; //请求时的处理
R I L _ R a d i o S t a t e R e q u e s t onStateRequest; //状态请求时的处理
RIL_Supports supports; //返回这个实现的特殊请求
RIL_Cancel onCancel; II 取消时的处理
R I L _ G e t V e r s i o n getVersion; //获得版本信息
I RIL_RadioFunctions;

其中最重要的是RIL _ RequestFunc, 由该接口实现具体请求的分发和处理,其request


参数表示以RIL_REQUEST_为开头的请求命令。
RIL _ RadioFunctions结构需要由RIL实现库提供实现,通过RIL_Init返回rild后,rild
会调用以下函数完成注册:

�274
第16章电话系统 oo•

v o i d R I L _ r e g i s t e r (const RIL_RadioFunc豆ons * c a l l b a c k s ) ;
p a c k a g e com. a n d r o i d _ . i n t . _ e r n a l . t e l e p h o n y ;

RIL_register()函数完成接口的注册。至此,rild到RIL实现库的请求发送路径和反向的
响应路径就已经构建了,进入正常运行的状态。因此,RIL_ RadioFunctions是RIL接口中
最重要的部分,需要RIL实现库来实现其中的函数指针。
初始化完成后,rild守护进程和RIL实现库就可以进行运行时的交互,主要体现在两
个方面:第 一 个方面 由 rild 发起 ,调用各种请求 CR IL_R E Q U E ST ), RIL实现库完成请求
后,返回,rild中的OnRequestComplete()接收请求结果;第二个方面由RIL实现库主动上
报,由rild中的OnUnsolicitedResponse()接收主动上报的结果。

一 提 示 : 当 需 要 在 电 话 部 分 接 口 中 增 加 R I L 命 令 时 , R I L _ E n v 和 R I L_ RadioFunctions
等结构及其中的函数也不需要更改。

16.2.3 Java层中的电话部分
电话系统的Java核心部分的代码在框架层的电话包和Phone应用中。从运行的角度,
包括几个方面的内容: "Phone"服务运行于Phone应用进程,''telephony.registry"服务运行于
系统服务器进程,框架库中的各个类运行于调用者的进程。

1. Java框架中的电话部分
Java框架层中的电话部分包括公共包(android.telephony)和内部包(com.android
internal.telephony)两个部分。它们各自又包含gsm和cdma的子包。
android.telephony包中作为API使用的类如下所示。
e TelephonyManager: 电话部分的总管类,用于获得使用电话系统的信息,包括电话
网络的ISO标准的国家码、MCC和MNC代码、移动网络运营商的名字、网络类
型等。
e ServiceState: 表示电话状态的类,包括在线、不在线、紧急呼叫等。
e PhoneStateListener: 类用于监听电话的状态,可以注册到TelephonyManager中。
e PhoneNumberUtils和PhoneNumberFormattingTextWatcher: 用于电话号码方面的辅助
处理。

PhoneFactory是 个 android.t e l e p h o n y 包 中 的 非 A P I 的 隐 藏 类 , 提 供 了 部 分
Telephony Manager的实现。
从实现的角度,RIL.java是Java层和底层的接口,定义了RILRequest和RIL类。RIL
类也实现了Commandslnterface, 并为android. telephony包中的PhoneFactory部分调用,然
后建立了PhoneProxy作为GSM和CDMA电话的代理实现。
在RIL类的构造函数中,建立名为RILReceiver的线程实现,主体流程如下所示:
s t a t i c f i n a l S t r i n g SOCKET_NAME_RIL = " r i l d " ;
c l a s s RILReceiver implements Runnable {
byte [l buffer; / /表示缓冲区
R I L R e c e i v e 工( ) { b u f f e r = new b y t e [ R I L MAX COMMAND B Y T E S ] ; · }

275�
• O O Android板级支持与硬件相关子系统

public void run() (


int re七ryCount = 0;
t r y {for (; ;) ( //通过Socket进行交互的循环
L o c a l S o c k e t s = null;
LocalSocketAddress l;
try {
s = n e w LocalSocket(); //建立通信的Socket
1 = new L o c a l S o c k e t A d d r e s s ( S O C K E T N A M E RIL,
LocalSocketAddress.Namespace.RESERVED);
s.connect(l);

II省略异常处理部分
try {
I n p u t S t r e a m is = mSocket.getinputS七ream(); //准备输入流
for (;;) { //再循环中进行处理
Parcel p;
length = readRilMessage(is, buffer); //读取RIL消息
if (length < 0) { b r e a k ; )
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(O);
processResponse(P,); //进行处理
p.recycle();


//省略异常处理部分
setRadioState (RadioState.RADIO_UNAVAILABLE);
//省略:关闭Socket, 消初状态
} } catch (Throwable tr) (//其他没有捕获的异常}

RILReceiver完成了与rild通过名称为"rild"的保留套接字通信。其中调用的
readRilMessage()方法读取对 一 个 R IL 消息输入流头部 , processR esponse()
并返 回消息 的长度 。
则完成对消息的响应处理:它调用的processUnsolicited()方法和processSolicited()方法完成
了对RIL命令的响应。
ITelephony.aidl是 一 个接 口文件 ,其 中定义 的主要 内容如 下所示 :
import android.as.Bundle;
import java.util.List;
import android.telephony.NeighboringCellinfo;
interface !Telephony {
v o i d d i a l ( S t r i n g number);
v o i d c a l l ( S t r i n g number);
boolean showCallScreen();
boolean showCallScreenWi七hDialpad(boolean showDialpad);
II省略内容......

此接口相当于Android系统电话部分的APL它被Java框架层和Java应用层的众多部
分使用,主要的调用是TelephonyManager, 也在关机功能、联系人、声音服务等中被调用。
!Telephony的实现是 一 个名称 为"phone"的服务 。

一提示: RIL底层提供命令形式的接口,而ITelephony.aidl则是功能性的接口。

�276
第16章电话系统 ooe

2. Phone应用程序包
Android系统电话部分的另外 一 个重要方面的内容在 Phone 应用程序包中。按照一 般
的Android系统启动流程,"phone"是最后ServiceManager注册的服务,这种在 一 个应用程
序包Capk)中调用addServiceO进行注册服务的方式,在Android系统中用得很少。实际上,
Phone应用程序包履行了 一 部分 Java 框架层的功能,实现 !Telephony 接口供其他方面调用。
其中PhonelnterfaceManager.java文件定义的类PhonelnterfaceManager继承了
!Telephony. Stub, 是!Telephony接口的实现,提供了名称为"phone"的服务,并在其publishO
方法中进行了注册,如下所示:
p u b l i c c l a s s P h o n e l n t e 江 a c e M a n a g e 工 e x t e n d s ! T e l e p h o n y . S t u b { //继承实现
private void publish() { / / 在 S e r v i c e M a n a g e r 中进行注册
S e r v i c e M a n a g e 立 ad d Se r v i c e ( " p h o n e " , t h i s ) ;

所实现的dial()方法如下所示:
p u b l i c v o i d d i a l ( S t r i n g number) (
S七ring u r l = c r e a t e T e l U r l ( n u m b e r ) ; //根据表示号码的字串,创建URI
i f ( u r l == n u l l ) { r e t u r n ; l //错误处理
P h o n e . S t a 七 e s t a t e = mPhone. g e t S t a t e ( ) ; / / mPhone 是 Ph o n e 类型的接 口
if ( s t a t e ! = P h o n e . S t a t e . O F F H O O K && s t a t e ! = P h o n e . S t a t e . R I N G I N G ) {
I n t e n t i n t e n t = new I n t e n t ( I n t e n t . A C T I O N _ D I A L , U r i . p a r s e ( u r l ) ) ;
i n t e n t . a d d F l a g s ( I n t e n 七 . F L A G A C T I V I T Y NEW T A S K ) ;
mApp.startActivity(intent);

在dialO函数中,首先需要根据号码的字串,创建包含"tel:"前缀的URL, 这是为Android
应用层中的Intent所使用的URI的协议名。然后创建响应的Intent的action, 以URI作为
Intent的data, 发送Intent, 启动响应的Activity。这里的各个Activity主要也是在Phone应
用程序包中实现的。
因此,"phone"服务的实现也调用UI相关的功能,这也是它在Phone应用程序包中实
现,而不是在Java框架层中(如系统服务器)实现的原因。
3. 电话注册服务
在系统服务器C SystemServer)中,注册了 一 个名称为"telephony.registry"的服务, 实
现的内容是com.android.server包中的TelephonyRegistry。
Telephony Registry类继承了ITelephonyRegistry.Stub。而ITelephonyRegistry是由
com.android.internal. telephony中的ITelephonyRegistry定义的AIDL远程接口。
ITelephonyRegistry.aidl定义的ITelephonyRegistry接口如下所示:
interface ITelephonyRegistry {
v o i d l i s t e n ( S t r i n g pkg, IPhoneStateListener c a l l b a c k ,
i n t events, boolean notifyNow);
v o i d n o t i f y C a l l S t a 七 e ( i n t s t a t e , S t r i n g incomingNumber);
void notifyServiceState(in ServiceState state);
v o i d n o t i f y S i g n a 廷;t r e n9 t h (i_Il S i g 庄 a l S t r _ e n g t h s i g f ! . l 廿 . S t r e n g t h ) ;

277�
• O O Android 板级支持与硬件相关子系统

void no七ifyMessageWai七ingChanged(boolean mwi);


void no七ifyCallForwardingChanged(boolean cfi);
void notifyDataActivity(int state);
void n o t i f y D a t a C o n n e c t i o n ( i n t sta七e, b o o l e a n i s D a t a C o n n e c t i v i t y P o s s i b l e ,
S t r i n g r eason, S t r i n g apn, i n S t r i n g ( ] apnTypes,
S t r i n g i n t e r f a c e N a m e , i n t n e t w o r k T y p e , S t r i n g ga七eway);
void notifyDataConnectionFailed(String reason);
v o i d n o t i f y C e l l L o c a t i o n ( i n Bundle c e l l L o c a t i o n ) ;

listen()方法用于监听 ,各个 notifyXXX() 方法用于通知 ,主要 为呼叫状态 (C allState)


和服务状态 (ServiceState) 的通 知 。
!Telephony Registry 的调用主要来 自两个方面 :作为 A PI 的 TelephonyM anager 类 中监听
电话状态的 listen()就 是 通 过 !TelephonyRegistry 的 liste顶) 方 法 实现 的 ; 电话 内部 包 的
DefaultPhoneNotifier 封装并调用 ITelephonyR egistry 的各个 notifyX 劝(()方法 。

16.3 电话BSP的结构

电话部分移植的内容主要是两个部分: M o d e m 驱动程序和 R IL 硬件抽象层 。


目前的外置 3G M odem 大多通过 U SB 来完成连接 , 使用 U SB 转 Serial 的接 口。
针对 U SB
转 Serial 的驱动程序 一 般都使用标准驱动 。但针对特殊的 M odem 型号 ,以及硬件设计 ,需
要实现不同的电源和重启操作,也可能需要自行开发其 U SB 或 Serial 的驱动程序 。
R 且 硬件抽象层 是需要重点关注 的移植部分 ,大部分外置 M odem 都通过 AT 命令与应
用处理器通信,因此硬件抽象层实现的主要功能是转换 R IL 定义的命令 为具体 AT 命令实
现。A ndroid 自带 的 reference-Iii包含 了绝大部分标准 A T 命令的实现 。 但针对大部分 M odem ,
特别是 3G M odem , 需要移植的工作量依然很大。

�16.3.1 驱动程序

在移动电话系统中, M odem 般也被称为基带 , 外置 M odem
分为外置和芯片集成两种 。
通过 U A R T 、U SB 等方式与应用处理器连接 ,而 内置 M odem 常常使用共享 内存等方式进
行通信。不同实现方案的处理器不同,有些处理器需要连接外置 M odem 才 能做成手机 ,有
些处理器则将 M odem 集成到其 中。
模块化的 M odem 接 口一 般都做得非常简单 。核心 的控制主要是 电源 、重启 、飞行模式 、
A T 及数据通道 的接 口。因此 ,驱动程序往往非常简单 。
电源和重启方面, 一 般用单独 的 G PIO 进行 开关操作 ,部分 M odem 也是通过 G PIO 来
操作进入飞行模式。因此在驱动中需要加入对应实现,并暴露接口给用户空间。
Modem 一 般 分 AT 命令通道和数据通道两路 。部分只有单路通道 的,需要通过 M ux 协
议将两路数据整合。因此,外置 M odem 驱动层 的主要工作是实现 M odem 的 电源/
重启管理 ,
以及这两类接口的驱动程序。而内置 M odem 一 般在驱动层搭建 了基于共享 内存 的专用通信
协议,不 一 定使用传统 的 AT 命令等 ,例如 ,高通 的方案具有 PR O C C O M M 、SM D 、O N C R PC
等协议用于 M odem 与应用处理器 的通信 。
U S B 转 Serial, 以及 Serial 的驱动 ,主要定义在 Linux 内核 的如下路径 中。

�278
第16章电话系统 oo•

a drivers/usb/serial/: U S B 串 口 部 分 。
e drivers/serial/: 串口部分。
AT和数据通道的接口,大多Modem都是用USB转Serial的标准实现,因此可以使用
drivers/usb/serial/目录中的option.c来完成。
o p t i o n . c 定 义 如 下 o p t i o n _ lport_ device设备:
static struct usb serial driver option lport device = {
.driver = {
.owner = THIS MODULE,
.name = "option!",
),
.desc豆p已on = "GSM modem (1-po 工t )"'
.usb_d豆ve工= &option d豆ver,
.id table = option_ids,
.num王orts = 1,

.open = option_open,
.close = option_close,
.w工ite = option write,
.write_room = option_w豆te_room,

.chars_in_buffe工 = op七ion_chars_in_buffer,
.set termios = option set termios,
.tiocmget = option_tiocmget,
.tiocmset = option_tiocmset,

.attach = option ,.startup,


.shutdown = option shutdown,

.read_int_callback = option_instat_callback,
);

该 驱 动 通 过 o p t i o n _init时注册成为usb_serial_driver, 再 通 过 对 u s b _ d r i v e r 的 注 册 , 响
应 对 u s b 设 备 的 枚 举 。 对 应 的 u s b driver为:

static struct usb_driver option_driver = {


.name = "option",

.probe = usb serial probe,

.disconnect = usb serial disconnect,


ltifdef CONFIG PM
.suspend = usb_serial_suspend,
.resume = usb serial resume,
ltendif
.id_七able = option_ids,

.no dynamic id = 1,
);

因此,为了匹配枚举的USB设备,这里需要定义VENDOR ID和PRODUCT ID。option.c


中已经定义了非常多的支持设备,只需要在数据里添加设备IDs即可。该数据结构为:
static struc;_t usb device_ id optic旦_ids[]

定义好两个ID后,将如下格式的结构体添加到该数组中即可,如下所示:

至此,当Modem电源被开启后,在用户空间应该会出现/dev/ttyUSBO和/dev/ttyUSB 1
这两个端口设备。

279�
•oO Android板级支持与硬件相关子系统

提示:部分Modem可能需要使用专用USB转Serial应用,而设备端口的名字也可
能不一样。在rild服务开启的时候,需要注意对端口的使用。

另 一 种方面是省 电的处理 ,M odem 的省 电策略 比较 复杂 。不仅 因为 M odem 本身有相


应的飞行模式等状态,而且大部分Modem使用了USB接口,需要同时处理如USB P H Y
等芯片的低功耗模式,部分Modem在低功耗模式下,甚至需要额外处理来电或者短信时的
中断唤醒应用处理器等操作。

16.3.2 RIL实现库接口(作为硬件抽象层)
RIL实现库的接口比较复杂,需要包含整个电话功能。但整体看来,复杂程度主要体
现在需要维护的状态、需要处理的命令,以及相关的结构体等,并不是代码逻辑上的复
杂。ril.h中定义了该接口,同时,该目录下的ril_cdma_sms.h作为对CDMA协议的补充
而存在。
ril.h中定义了百余种请求消息类型,包含通话、短信、网络信息查询等方面。以
RIL_REQUEST_为开头,部分内容如下所示:
#define R I L REQUEST DIAL 10 //拨打电话
#define RIL_REQUEST_HANGUP 12 //挂断
#define R I L REQUEST SIGNAL STRENGTH 19 //信号强度
#define R I L REQUES'!'_REGISTRATION至TATE 20 //注册状态

ril.h中还定义了数十种自动上报(unsolicited)消息类型,以RIL_UNSOL_为开头,部
分内容如下所示:
#define RIL_UNSOL_SIM_SMS_STORAGE_FULL 1013 // SIM卡的短信息满
#define R I L UNSOL ON USSD 1 0 0 6 //得到新的USSD信息
鲁define R I L UNSOL RESPONSE NEW SMS 1003 //响应新短信息
#define R I L UNSOL RESPONSE NETWORK STATE CHANGED 1002 //响应网络状态改变

这些消息类型基本上覆盖了常用电话功能的各个方面,移植需要实现这些类型所对应
的请求和响应,并将其对应到所使用的Modem上。 一 般情况下 ,除非确 实有额外 的功能需
要实现,否则并不建议对其进行扩充。

提示:消息分为两类: unsolicited是主动上报的消息,如来电、未短信等; solicited


是AT命令的响应。

除此之外,ril.h中主要定义了这些消息类型所需要的特定结构体,命令可以使用各种
不同的结构来传递信息。例如,RIL_REQUEST_DIAL需要如下的结构体:
七ypedef struct {
char* address;
int clir;
R I L UUS Info* uusinfo;
I RIL Dial;

每个结构体的定义都和具体的RIL命令相关,也可以根据需要自行定义。

280
第16章电话系统 oo•

16.4 电话BSP部分的实现

�16.4.1 RIL的参考实现
电话系统与硬件相关的部分是RIL的实现库,在不同的硬件中该库的结构类似,均来
源于RIL库的参考实现(libreference-ril.so)。在移植过程中,通常不用更改RIL参数实现
的结构,以及其中的处理流程。针对具体的Modem硬件,主要的工作通常是根据对应Modem
的软件接口差别,对RIL库的参考实现进行修改或增加。

1. 屯刀女台1七
RIL库的参考实现支持多种端口,可以配置选择socket、TTY节点等几种方式。对 一
般的USB转Serial接口或者Serial接口来说,选定正确的TTY节点,通过参数传入即可。
进入RIL_Init后,将开启另 一 线程执行 m ainL oopQ 函数 ,m ainL oopQ 是线程 的实现 。
该线程负责打开端口,并保证在端口意外关闭等情况下,重新打开端口。
mainLoopQ函数中需要注意的片段如下所示:
} e l s e i f ( s _ d e v i c e _ p a t h ! = NULL) (
f d = o p e n ( s _ d e v i c e _ p a t h , O_RDWR);
if ( f d >= 0 && ! m e m c m p ( s _ d e v i c e _ p a t h , " / d e v 几 t y S " , 9 ) ) (/ /端口的选择
struct termios ios; //通过设登 t e r mi o s 串口的回显
tcgetattr(fd, &ios);
i o s . c _ l f l a g = 0;
tcsetattr(fd, TCSANOW, & i o s ) ;

这里完成对实际端口的打开工作,需要针对自己的Modem配置流控等初始化控制设
置。随后,打开后的文件句柄就交由AT命令部分处理,通过at_open函数,完成此操作。
Android将AT命令的 一 部分公共 内容单独放在 一 个 atchannel.c 中,例如错误码分析 、读写 、
解析等操作。但特定的命令解析依然在reference-ril.c文件中完成,例如,从
RIL_ R E Q U E S T _D I A L 到 具 体 的 A T 命 令 A T D 。
至此,RIL已经可以向Modem端口写入命令,以及获取响应了。但初始化刚刚开始,
通常在Modem的端口可用之后,还需要对其写入基本的AT配置命令,libreference-ril实现
库通过执行血tializeCallback()函数完成。但其运作机制还是和AT命令的请求和响应流程
密切相关。

2. A T 命 令 处 理 流 程
AT命令是Modem标准的软件接口。进化到无线网络后,无论是GSM还是CDMA,
还是3G等通信协议,依然沿用此套协议,定义自身对应的标准AT命令。同时,Modem
厂商往往根据自身需求,定义所需要的扩展AT命令。
使 用 A T 命 令 访 问 M o d e m , 通常具有如下特点。
o以"AT"作开头,字符作内容,字符结束符作为命令结束符。

281
•oo Android板级支持与硬件相关子系统

o每条命令都有特定的响应,例如成功I失败、返回信息等,同样有结束符。
o部分命令需要处理多行响应,例如收取短信等。
• Modem也会有主动上报的信息,例如来电、来短信等。

o同 一
时间 ,一 般只允许处理 条 A T 命令 ,字面含意为串行 。
因此AT命令的处理,在设计上相对容易。串行的处理模式从很大程度上避免了设计
的复杂性。而明确的响应信息和结束符可以使开发者通过简单的字符串解析即可方便地做
到响应解析。
Android中AT命令解析的实现主要在atchannel.c中,命令的发送方面包括以下函数:
int a t _ s e n d _ c o m m a n d _ f u l l _ n o l o c k (const char *command, A T C o m m a n d T y p e type,
const char *responsePrefix, const char *smspdu,
long long timeoutMsec, A T R e s p o n s e **pp_outResponse);
int a t _ s e n d _ c o m m a n d _ s i n g l e l i n e (const char *command,
const c h a r *responsePrefix, A T R e s p o n s e **pp_outResponse);
int a t _ s e n d _ c o m m a n d _ n u m e r i c (const char *command, A T R e s p o n s e **pp_outResponse);
int at send c o m m a n d m u l t i l i n e (const char *command,
c o n s t c h a r *responsePrefix, A T R e s p o n s e **pp_outResponse);
int a t _ h a n d s h a k e ();
int a t _ s e n d _ c o m m a n d (const char *command, A T R e s p o n s e **pp_outResponse);
int at send c o m m a n d sms (const char *command, c o n s t c h a r *pdu,
c o n s t char *responsePref土x, AT胆sp_.p芒9 了 p p立 u tR e sp o n se ) ;

对千单行命令,除at_bandsbakeO直接调用最终的at_send_command_ full_no—lockO函数
外,这些接口都通过调用at_send_command_ fullO进行串行化后,再调用at_send—command_
full_no_lockO完成最终发送。针对不同的命令,传递不同的类型,以便在处理响应时可以
做出不同处理。例如,at_send_c o m m a n d O 通 常 用 来 发 送 仅 有 O K 或 者 E R R O R 响 应 的 A T
命令,进行某些配置。at_send_command_singleO通常用来发送具有单行响应的AT命令,
例 如 读 取 某 些 配 置 , 这 类 命 令 同 样 也 有 O K 或 E R R O R 指 示 是 否 成 功 , a t _send_command_
singleO完成时,会携带响应信息返回。
以上的函数都是同步函数,直到获取到响应信息,或者超时前,只能通过取消来使其
退出。同步的处理方式也是简化串行操作的常用方法。
send操作通常是由rild的event处理模块发起的。当send完成以后(即通过打开的端
口写出以后),一般是将send线程挂起,等待read线程的工作,直到获取到需要的响应信
息,或者超时。read线程在at_openO中,通过创建新线程,执行readerLoopO循环实现。
readerLoopO函数通过调用readline()函数,逐行读取Modem端口发来的信息,进行解
析。因为AT命令的多样性,解析的过程逻辑比较简单,但需要处理的情形是相对复杂的。
前面提到在send命令中,都有设置对应的命令类型,例如单行、数字、多行等。readerLoop()
中调用的processLine()函数负责根据这些类型,完成响应信息的解析。直到获取到需要的
信息,方激活等待的send线程,使其返回获取的内容。
对于某些具体的实现,这部分发送和接收的代码 一 般不需要做太 多修 改 。已经 能处理
各种AT命令。但需要大致清楚AT处理流程,并了解其接口。此外,线程间同步的处理内
容与processLine()函数的实现相关。

�282
第16章电话系统 ooe

提示:该部分可能会有一些跟省电相关 的逻辑需要实现。

3. Event 模块
在 reference-ril.c 的 m ainL oopO 函数 中,具有如下调用 的代码 :
R I L reguestTimedCall_back ( 罕 i a l i z e C a l l b a 吗 N U L L , �TIMEVAL_O);

m山 alizeC allback() 函 数 用 于 发 送 给 M odem 初 始 化 的 AT 命 令 。这 里 的


RIL _requestTimedCallback 传入的 T IM E VA L_O 其实是 o, 没有起到多少延时作用。这里的
主要作用是切换线程运行 initializeC allback(), 使其能在 Event 模块 中执行 。
Event 模块在 libril.so 中实现 ,rild 通过 R IL_ startEventLoop() 函数创建基于 eventLoop
的新线程。 Event 模块 的事件处理 ,都 在该线程 中排 队 串行完成 。
跟大多数事件处理机制 一 样 ,E vent 模块定义 自身的 E vent 结构 ,并用链表 的方式来管
理排队的 Event。提供 add 、delete 接 口,以及 callback 函数原型 。事件结构如下所示 :
struct ril event {
struct ril_event *next; //表示链表的后 一

struct ril_event *prev; //表示链表的前 一

int fd;
i n t index;
bool persist;
struct timeval timeout;
r i l event cb func;
v o i d *param;
};

事件回调函数的原型如下所示:
tye咚吐void一{*ri上event_cb) {辽比fd, 过 沪 r t _eye!Ji.s, 又立p. *userd_a_ta >.;

Event 模块 的事件 是通过获取 需要 关注 的文件句柄上 的信 息而触发 的。A ndroid ril 使用


socket 接 收 上层传入 的命令 ,因此定义 了一 个 E vent, 将其关注的文件句柄设置为 socket,
当 socket 上有连接动作时 ,该事件被激活 ,从而建立连接 以接收命令 。而建立 的连接 socket
同样通过事件机制监听,当该 socket 上有数据输入时 ,激活命令接收事件 ,处理相应命令 。
因此 E vent 模块 的核心其实是基于 select 多路选择机制构建 的事件处理机制 。
相关代码如下
所示:
v o i d r i l _ e v e n t _ l o o p () {
int n;
fd set rfds;
struct timeval tv;
struc七timeval * ptv;
for (;;) { // 事件循环
memcpy(&rfds, &readFds, s i z e o f ( f d _ s e t ) ) ; //复制用千读的 f d_ s e 七
if ( - 1 == c a l c N e x t T i m e o u t ( & t v ) ) { I I 计 算 下 一 次超 时
p t v = NULL;
} else { p t v = &tv; }
printReadies(&rfds);
n = s e l e c t ( n f d s , & r f d s , NULL, NULL, p t v ) ; //进行多路选择
printReadies(&rfds);
I I .. ,_, ... 省略部分错误处理内容

283�
• O o Android板级支持与硬件相关子系统

p r o c e s s T i m e o u t s {) ; //超时方面的处理
processReadReadies(&rfds, n); //处理用千读的fd_set
firePending();

processReadReadies即完成需要激活的事件,添加到激活队列的工作。同时,Event模
块还维护 一 套 tim er 机制 ,不通 过文件 句柄 ,而通 过 tim er 超 时来触 发 。上 面代码 中的
processTimeouts完成添加需要激活的事件到激活队列的操作。
Event模块即是上述两种触发方式所驱动的事件处理机制,在RIL中起到核心的作用。
最终由firePending函数完成调用该激活事件的回调函数。

4. M o d e m A T 命 令 初 始 化
initializeCallbac岚)函数通过RIL _requestTimedCallback函数,透过Event机制调用,完
成对Modem的AT命令初始化工作。
该部分工作是在移植时需要实现的重点。通常情况下,这个函数只需要完成 一 系列 A T
命令的发送即可。但也有部分Modem需要根据 一 些 响应判 断是否初始化成功 。其 中包括 了
对at_handshake的调用,这个函数的实现在atchannel.c中,但同样需要根据自己Modem的
情况,给予实现。
AT命令的发送,通过前面介绍的at_ send_ command及配套的其他接口实现。因为该接
口是同步的,因此可以就地处理反馈的信息。如果发现失败,需要采取相应措施,例如重
发、甚至重启整个流程等。可以参考Android的该函数实现,初始化自己的Modem。
该部分的移植并不需要上层挂钩,因此相对独立。只要根据初始化情况,更新维护在

RIL中的Modem 些状态信 息即可 。初始化完成 以后 ,M odem 已经可 以接收上层命令 ,
标志性的信号为isRadioOn函数返回1。这个函数通过"AT+CFUN"命令进行判断。

5. Request (请求)流程
libril库当中的ril_ event_loop()函数负责处理来自上层的socket连接请求,并处理发来
的RIL命令。此函数被rild守护进程使用。
请求的流程是:从Android框架Java层,通过Socket将命令发送到RIL层的rild守护
进程,rild守护进程中,负责监听的ril_ event_loop消息循环中的Select发现RILD Socket

有了请求链接信号,会建立起 个 record_stream , 打通与上层的数据通道并开始接收请求

数据。数据通道的回调函数processCommandsCallback()会保证收到 个完整 的 R equest 后
(Request包的完整性由record_ stream的机制保证),将其送达processCommandBuffi叫)函数。
该函数完成具体的分发(dispatch), 这是命令的请求流程。
要保证命令的分发,需要命令类型、命令分发处理函数,以及命令响应处理函数,定
义结构Commandlnfo, 在ril.cpp中有以下内容:
typedef struct {
int reques七Number;
v o i d (*dispatchFunction) (Parcel &p, struct R e q u e s t i n f o *pRI);
int (*responseFunction) (Parcel &p, v o i d *response, size_t responselen);
Commandinfo;

�284
第16 章 电话系统 ooe


所有命令的Commandlnfo信息形成 个 数 组 , 定 义 在 ril.cpp 中 , 内 容 通 过 inc lu d e
ril_ c o m m a n d s . h 引 入 。 以 下 是 几 个 典 型 例 子 :
{RIL REQUEST GET SIM STATUS, dispatchVoid, responseSimStatus),
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
{RIL REQUEST SEND SMS, dispatchStrings, responseSMS},
{RIL REQUEST SETUP DATA CALL, dispatchStrings, responseStrings},

不 同 的 命 令 类 型 有 不 同 的 分 发 处 理 方 式 , 例 如 , d i s p a t c hV o i d 处 理 不 带 参 数 的 A T 命 令 。
也有不少命令类型,需要专门的分发处理,例如RIL_REQUEST_DIAL, 就由自己专门的
邮patchDial处理。这主要还是因为AT命令本身的多样性和结构的复杂性,之前提及的ril.h
中定义的各种结构体,也是为此而存在的。同理,命令响应处理函数,也会根据不同的AT
命令,做不同的处理。
典型的dispatchXXX中,根据上层传来的数据流,解析出需要的参数。并最终通过
RIL_Init获得接口中的onRequest接口,调用reference-ril.c中的具体实现。
onRequest的原型为:
static void onRequest (int request, void *data, size_t datalen, RIL_Token t);

onRequest负责具体命令的分发,分发到实现最终匹配modemAT命令的函数中去。
例如,RIL_REQUEST_DIAL的实现如下所示:
case RIL REQUEST DIAL:
requestDial(data, datalen, t};
break;

requestDial()函数的实现如下所示:
static void requestDial(void *data, size t datalen, RIL Token t){
RIL Dial *p dial;
char *cmd;
const char *clir;
int ret;
p_dial = (RIL Dial *)data;
swi七ch (p_dial->clir) {
case 1: clir = "I"; break; II invocation
case 2: clir = "i"; break; II suppression
default:
case 0: clir = " " ; b r e a k ; // subscription default

asprintf(&cmd, "ATD马s号s;", p_dial->address, cli工);


ret = at ....
send command(cmd, NULL);
free (cmd);
RIL_onReques七Complete(t, RIL_E_SUCCESS, NULL, 0);

该 函 数 调 用 a t _send_ c o m m a n d 发 送 " A T D x x x x " 命 令 拨 号 , 并 获 取 返 回 值 。 最 终 需 要


调 用 R I L _ onRequestComplete, 完 成 请 求 。 此 时 也 就 进 入 了 响 应 流 程 。

6. Response流程

Response ( 响 应 ) 有 两 类 : u n s o l i c i t e d 表 示 主 动 上 报 的 消 息 , 主 动 上 报 的 意 思 就 是 非 针
对响应,如来电、来短信等;而solicited是AT命令的响应。从结果方面判断是否是solicited

285�
•oo Android板级支持与硬件相关子系统


的依据有两点: 是当前有 A T 命令正在等待响应 ;二是读取到 的响应符合该 A T 命令 的响
应格式。
响应从AT命令流程的read环节开始。对于solicited部分,RIL_ onRequestComplete()
是实现完成处理的函数,该函数同样是RIL_Init时获得的Env中的接口。而对于unsolicited
主动上报消息,processLine直接回调reference-ril.c中的onUnsolicited()函数。由该函数调
用RIL_Init时注册的Env的RIL_ on U nsolicitedResponse()完成处理。
RIL_ onRequestComplete中,完成对Commandlnfo中responseFunction接口的回调。实
现对不同命令的响应信息,做出不同处理。这里的处理,主要是转换成RIL定义的响应信
息结构。随后,统 一 通过 sendR esponse, 最终从命令socket将响应结果反馈到上层。
RIL_ on U nsolicitedResponse通过ril.cpp中定义的另 一 结构 U nso!Responselnfo, 寻找对
应的处理函数:
七ypedef struct {
int requestNumber;
int (*responseFunction) (Parcel &p, v o i d *response, size_t responselen);
W a k e T y p e wakeType;
} UnsolResponse_Info;

这是与Commandlnfo类似的结构,不同的是,因为是主动上报,只有responseFunction。
另外增加了 一 项 ,标 明需要 占用 的 w akelock , 以决定是完全唤醒手机(例如来电),还是仅
在earlysuspend状态下工作。UnsolResponselnfo同样有 一 个数组 ,维护各种 unsolicited 响
应所对应的处理函数。数组定义在ril.cpp, 通过include ril_unsol_comrnands.h引入。
RIL _ onUnsolicitedResponse完成与RIL_ onRequestComplete类似的操作,先调用
UnsolResponselnfo定义的对应responseFunction接口,再通过sendResponse, 最终从命令
socket将响应结果反馈到上层。

7. 在RIL参考实现中扩展特定命令
熟悉命令的请求和响应流程后,可以根据需要适配Modem的命令。在此假设的Modem
不是通过at+csq获取信号强度,而是通过at+sig获取信号强度。由于命令本身是标准的,
因此不需要扩展ril.h中的接口,只需更改底层AT命令实现。
首 先 , 需 要 找 到 R I L 中 对 应 的 命 令 类 型 : 即 R I L_ R E Q U E S T _ S I G N A L _ S T R E N G T H ,
它的Commandlnfo结构在ril_commands.h中定义如下:
{ R I L _ R E Q _ U E S T 主 邻 A L__§1'.RENGT且仁d_ispa压: hVoic!, _ res,P,足咚eRil5_iq且色lStre1:1gth),

这个命令没有参数,因此可以使用dispatchVoid作为分发函数。无须更改此处。如果
需要自行实现如Dial等分发函数,需要在其中实现从上层数据流中解析出参数的逻辑,并
存放于特定结构中。
dispatch Void最终调用reference-ril.so中传入的onRequest完成分发。因此转到该函数
中,发现有如下代码:
case RIL_REQUEST_SIGNAL_STRENGTH:
requestSignalStrength(data, datalen, t);
break;

�286
第16章电话系统 ooe

因此实际处理该命令的是requestSigna!Strength函数。对于需求来说,没有必要更改此
函数的名字。requestSigna!Strength()函数的内容如下所示:
static void requestSignalS七rength(void *data, size_t datalen, RIL_Token t)

ATResponse *p_response = NULL;


int err;
int response[2];
char *line;
e r r = at_send_command_singleline("AT+CSQ", "+CSQ:", &p_response);
//省略部分内容
RIL_onRequestComplete(t, RIL_E_SUCCESS, response, sizeof(response));
at_response_free(p_response);
return;
error:
LOGE("requestSignalStrength must never return an error when radio is on");
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
at response free(p response);

在此看到,该命令实际发送AT+CSQ到Modem, 因此更改CSQ为需要的SIG。发送
正确的命令到Modem。需要获取单行响应信息AT+SIG=xxx。因此,send命令保持不变。
接下来需要检查send命令的返回值,因为假设的Modem只更改了AT命令名,并未

修改返回值结构。因此原来的检查依然可用,不用更改。在这 步 ,如 果 M o d em 返 回 不 一
样的数据,那么需要在此处,修改为与R1L接口相匹配的response。
最 终 , 仍 然 需 要 调 用 R I L _onRequestComplete()函数完成命令。这个函数中,需要完成
对Commandlnfo中responseFunction接口的回调。因此,需要检查R1L—REQUEST_
S I G N A L _ STRENGTH相关的responseFunction定义,也就是response沁lSignalStrength。

在不同的实现中此函数并不需要修改,因为在上 级 获 取 到 返 回 值 时 ,已 经 将 resp on se
修改为与ril相匹配的形式。

进 步 ,对 千 使 用 C D M A M o d e m 的 系 统 , 除 了 以 上 部 分 , 可 能 还 需 要 在 i n i t . r c 中 指
定ro. telephony.default_n e t w o r k 为 4 , 会 影 响 P R E F E R R E D — N E T W O R K _ MODE的设置,并
且会影响Android框架默认创建的Phone种类。可以使用的参数如下所示:

II*
* The preferred network mode 7 = Global
* 6 = EvDo only
* 5 = CDMA wlo EvDo
*****
4 = C D M A / EvDo auto
3 = GSM / WCDMA auto
2 = WCDMA only
1 = GSM only
0 = GSM / WCDMA preferred

�16.4.2 数据连接部分
数据连接部分其实也属于硬件抽象层的范畴,相对其他RIL命令的实现,它比较特殊。
数据连接需要完成两部分的移植工作: AT拨号,以及建立PPP连接。
大部分Modem需要首先通过AT命令拨上运营商提供的网关。因此在移植过程中,需

287�
• O o Android板级支持与硬件相关子系统

要配置相关的处理函数。
在Android RIL中,该函数为:
static v o i d reques七SetupData a_ll (vc,id 旦ata, size_七datalen, Ril:,_Token t)

需要根据自己的Modem进行配置,大多数GSMModem使用如下的标准AT命令进行
拨号:
a s p r i n t f (&cmd, "AT+CGDCONT=l, \"IP\",\" %s\", , 0, 0", apn) ;
e r r = at_send_command(cmd, NULL);
free(cmd);
e r r = at s e n d c o m m a n d ( " A T + C G Q R E Q = l " , NULL); //执行 A T 命令
e r r = at send command("AT+CGQMIN=l", NULL);
err = at_send_command("AT+CGEREP=l,0", NULL);
e r r = at_send_command("AT+CGACT=l,0", NULL);
err = at_send_command("ATD*99***1#", &p_response);
if Lerr < 0 I I p _ r e s p o n s e -,> s u c c e s s = = 0) { goto err.9r; } //错误处理

在最后 一 步 ,发送 'A T D *99***1#"进行拨号,通常网关会返回'CONNECT"作为成功信


号。可以在此处判断是否拨号成功。
而大部分3G模块通常实现了自己的拨号命令,例如AT¾DATA等,需要在此处替换
成自己需要的配置。
拨号完成后,Android利用PPP进行链路层连接,使用pppd进行操作。在Android的
版本中,pppd相关的网络连接部分代码变化非常大。不仅框架代码中增加了很多网络相关
的代码,netd下也实现了网络部分的daemon程序,用于处理和网络相关的本地操作。
netd对PPP的控制部分,主要在netd中实现,两个关键函数如下所示:
int a t t a c h P p p d ( c o n s t char *tty, struct in_addr local,
struct in_addr remote, struct in_addr dnsl, s七ruct in_addr dns2);
int d e t a c h P p p d ( c o n s t char *tty);

这两个函数分别表示联系到PPPD和解除到PPPD的联系,完成对/system/bin/pppd的
调用,并传入参数。
netd通过对外的接口是名称为"netd"的socket, 接收传入的命令。其中,接收的PPP
相关命令的格式为字符串。激活和停止的命令分别为:
p p p d a t t a c h t t y d e v local_addr remote_addr dnsl dns2
PPI? 也 g丘t豆乳 t t y dev

可以在RIL实现库的requestSetupDataCall()函数成功后实现这部分功能,通知netd进
行PPP连接,还需要通知netd停止PPP连接。通常情况下,此函数没有实现,每个实现自
行处理,用千响应RIL_REQUEST_DEACTIVATE_ D A T A _ CALL请求。

汕 16.4.3 Mock RIL


MockRIL用于仿制RIL, 主要用于测试,其代码结构如下所示:
ril/rnock-ril/
1-- Android.rnk
-- src
1-- cpp 41 C源代码的目录,生成libmock_ril.so

�288
第16章电话系统 oo•

1--generated
1--java # Java 代码文件
1--js # JavaScript 代码 文件包括 mock_ ril .js 等脚本
1--proto
、--python # Phython 代码文件

其 中 c p p 目 录 中 的 内 容 将 生 成 l i b m o c k_ril.so库,位于目标系统的/system/lib目录中。
一 一
这是 个 R IL 的 实 现 库 。 它 可 以 像 R IL 的 参 考 库 样 , 被 rild 守 护 进 程 使 用 。 例 如 在 目标
机的shell中通过设置环境属性,如下所示:
ii setprop rild.libpa七h /syste匣/lib /libm ock_ ril .so

重新启动 rild 可 执 行 程 序 , 此 时 作 为 R IL 实 现 库 的 就 是 lib m o ck_ril.so。


c p p / m o c k_ r i l . c p p 文 件 的 实 现 的 入 口 部 分 如 下 所 示 :
canst RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc,
int argc, char **argv) (
in七ret;
pthread_a七tr_t attr;
//初始化 JavaScrip t 引擎 (V S ) 的相关工作
v8: :VS: :Initialize() ;
v8: :Locker locker;
protobuf _ v8: :Ini七();
WorkerV8Init ();
v8::Persis七ent<v8::Context> c o n t e x t = makeJsContext();
v8::Context::Scope context_scope(context);
v8::TryCatch try_catch;
try_catch.SetVerbose(true);
ctrlServerinit (context); / / 初 始 化 C trl Server
s_rilenv = &testEnv; // RIL 实现库 的环境变堡 R IL Env
char *buffer;
int s t a t u s = ReadFile("/sdcard/data/mock_ril.js", &buffer); / / 打 开 JS 脚本
if (status == 0 ) {
runJs(context, &try catch, "mock_ril.js", buffer); //执行 JS 脚本
II省略错误处理部分

s_rilenv = env;
requestsinit(context, &s_requestWorkerQueue);
responsesinit(context);
RIL_register(&s_callbacks); // RIL 中本 身的回调注册 ,返 回 unso licited 主动上报
startMockRil(contex七); //调用 JavaScrip t 中的 star七 Moc kRil ()
return &s_callbacks; / / 返 回 R IL RadioFunctions 类 型的结 构 (回调 )


R I L _ l n i t ( ) 函 数 本 身 就 是 一 个 R IL 入 口 , 其 中 个 比较特 殊 的部 分 是初 始 化 了 V 8
JavaScript引擎和Ctrl Server, 然 后 打 开 了 m o c k 卫l.js 的 Jav aS crip t 脚 本 , 并 进 行 调 用 。
m o c k_ r i l . j s 和 相 关 的 J a v a S c r i p t 脚 本 在 j s 目 录 中 , 也 就 是 M o c k R I L 的 测 试 脚 本 。
startMockRil()函数将在被加载后调用。
ctrl_server是控制服务器,它可以进行消息的处理,依次作为与外部交互的手段。
ctr!_ s e r v e r 甚 至 可 以 在 其 他 的 电 脑 上 运 行 , 通 过 5 4 3 2 1 网 络 端 口 与 M o c k R I L 通 信 。 c p p 目
录中的ctrl_server. *文件是ctrl_server的核心实现,js目录中的ctrl_server.js是JavaScript层
的封装者。

289�
第17章
警报器— 实时时钟系统

17.1 警报器 — 实时时钟系统

Android的警报器 ( A la r m) 系统提供 了警报 和时间设置方面的支待 ,其实现 的硬件基


础通常是实时时钟(RTC)。在Linux内核代码中,需要有实时时钟设备驱动程序和Android
的Alarm驱动程序。警报器 — 实时时钟系统包含 了 JNI 和 Java 层 的接 口,在 Java 应用程序
层可以通过接口控制警报器方面的功能。
Android警报器 — 实时时钟 的相关 内容如表 17-1 所示 。

表17-1 Android 警报器— 实时时钟的相关内容


Android的层次 警报器 一 实时时钟 系统部 分 描 述

硬件层次 实时时钟硬件

橾作系统层 RTC驱动程序和Alarm驱动程序 Alann驱动程序为Android的标准

本地层 系统服务器中的JNl 被系统服务器的Java部分调用

Java框架层 AlarmManagerServ1ce 在 c o m android.server中,屈于系统服务器

Java层的API AlannManager 在android app包中,运行于应用进程

17.2 警报器 — 实时时钟子系统的结构

�17.2.1 总体结构
Android中的警报器 — 实时时钟系统结构如 图 17-1 所示 。
自下而上,警报器 — 实时时钟系统包含 了以下 内容 。
C 1) RTC驱动程序: Linux的实时时钟驱动程序
RTC驱动是内核中的标准驱动,在Android系统中,可以不使用其设备节点。
(2) Alarm驱动程序
第17章警报器 —
实时时钟系统 ooe

Alarm驱动是Android特定内核的组件,调用RTC部分的功能,但是本身和硬件无关。

a
二二二扭 一应
用一旦--- -=====一
一一
====-
= - ---- ---- -- ---- -- ----- --------------- ----1- -- -- -- ----- -

AlarmManager

Binder IPC
AlannManagerService
Jav谦架

----------
本地框架
AlarrnManagerService JN1

尸--------------------------------、
I::1
Linux内核层
Alarm

.. i RTC Driver
..
_____________________________________
图 17-1 Android 警报器— — 实时时钟系统的结构

(3) JNI部分
e frameworks/base/services/jni/com _ android—server_ AlarmManagerService.cpp
该文件是Alarm部分的本地代码,调用了Alarm驱动,也同时提供了JNI的接口。
(4) Java框架部分: Java服务库和Alarm.Manager类
e frameworks/base/services/java/com/android/server/ AlarmManagerService.java
AlarmManagerService.java文件实现了android.server包中的AlarmManagerService,
Alarm.Manager实现了IAlarmManager.aidl定义的基于Binder的接口,相关的代码路径:
frameworks/base/core/java/android/app/IAlarmManager.aidl
e frameworks/base/core/java/android/app/ Alarm.Manager.java
android.app包中的Alarm.Manager类是警报器Java层的平台API。
(5) Java的API和应用层
Alam沁tanager是警报器对Java层的平台APL在各个应用程序中可以调用它来控制警
报器和时钟,所有调用对应的是底层的同 一 实现 。

�17.2.2 JNI部分
Alarm在用户空间中的JNI部分由com android server AlarmManagerService.cpp 文件
实现,调用了Alarm驱动程序,向上层提供了JNI的接口。
Alarm JNI部分实现的主要方法列表如下所示:
s t a t i c J N I N a t i v e M e t h o d sMe七hods[] = (
{"init", " ( ) I " , {void*) android_server_AlarmManagerService_init),
{ " c l o s e " , " ( I ) V " , ( v o i d * ) a n d r o i d s e r v e r AlarmManagerService c l o s e ) ,
{"set", "(IIJJ)V", ( v o i d * ) a n d r o i d s e r v e r AlarmManagerService s e t } ,
{ "waitForAlarm", "(I) I",
(void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTimezone们"(II) I",
(void*)android_server_AlarmManagerService_setKernelTimezone},

291�
•oo Android 板级支持与硬件相关子系统

此处的本地函数提供的是对 android.server 包 中的 A larm M anagerService 类 的支持 。其


中 A larm M anagerService_ waitF or Alarm() 的实现如下所示 :
static jint android_server_AlarmManagerService_waitForAlarm(
JNIEnv* env, j object obj, j int fd) {
#if HAVE A N D R O I D OS
int r e s u l t = 0;
do{
result = ioctl (fd, ANDROID_ALARM_WAIT); // fd 是从 /de v /a la rm 中打开的
) w h i l e ( r e s u l t < 0 && errno = = EINTR);
//省略部分错误处理内容
return result;
#endif

实现初始化的 android_server_ AlarmManagerService _ in试)


函数打开 了 dev/alarm 设 备 ,
将得到的文件描述符返回。

汕 17.2.3 Java 部分
在 Java 代 码 方 面 , A larm M anagerService.java 文 件 实 现 了 android.server 中 的
AlarmManagerService 类 。它调用 了 JN1 部分 的代码 ,实现 了一 个 A ndroid 中的服 务。该服
务不作为平台的 A PI 使用 ,而是 一 个典型 的 B inder IPC 的服务 ,实现 了 IA larm M anager.aidl。
AlarmManagerService 运 行 于 Java 的服 务进 程 system —
server, 其中也会建立名称为
Alarm Thread 的线程 ,也包括 了 C lockR eceiver 等 几个广播接 收器 ,用于发送 Intent 进行通
知等工作。
AlarmManager.java 文件 实现 了 android.app 包 中的 A larm M anager 类 ,其提供 的方法和
aid! 接口定义的内容类似,作为平台的 A PI 使用 。同一 个包 中的内部类 C ontextlm pl通过 aid!
调用了警报器服务,而 A larm M anager 类 则调用 了 C ontextlm pl。
在系统的运行阶段 A larm M anager通常运行于应用程序 的进程 , 而 A larm M anagerService
运行于 Java 系统 公共 的系统服 务器进程 。

�17.2.4 Android系统时间方面的调用
Android 系统 的时间获取和设置方面 的功能与其他 的 L inux 系统略有 区别 ,A ndroid 系
统可以从 A larm 驱动 中获取系统 时间。
frameworks/base/libs/utils 目录 中的 System C lock.cpp 文件是本地工具库 libutils 的一 部
分,其中的 setC urrentT im eM ill函)
函数用于设置时间 ,主要 的 内容如下所示 :
int setCurren七TimeMillis(in七64_t millis) {
h f HAVE A N D R O I D O S
s t r u c 七 t i m e s p e c ts; // A l a r m 设备的 io c t l 中使用 的结构体
int fd;
int res;
#endif
int ret = O;
tv. t v ,sec

= (time t) (mill is / lOOOLL);
t v . t v usec = {suseconds_t) { { m i l l i s 沧 l O O O L L ) * lOOOLL);
#if HAVE A N D R O I D O S

�292
第17章警报器—实时时钟系统 ooe

fd = open("/dev/alarm", O_RDWR); // 打 开 A la rm 设备节 点


II省略部分内容
七s.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
res = ioctl (fd, ANDROID_ALARM_SET_RTC, &ts); I I获取实时时钟的时间
//省略部分内容
close(fd);
#else
if (settimeofday(&tv, NULL) != 0) { //通常使用的设置时间方面
ret = -1;

鲁endif
工e tu 亡 n et ;

I D 宏为真 的 情况下 (一 般情况 ),打开 Alarm 驱动程序 并通过调用


H A V E _ A N D R O—OS
标识为 ANDROID_A L A R M _ S E_TR T C的 ioctl 设置时间,而 HAVE_ANDROID_OS 宏 为假
的情况下,也可以使用 Linux 通常的 settimeofday()函数进行设置时间。
与之类似, elapsedRealtime() 用 于 获 取 时 间 ,可 以通 过 驱 动 程 序 并通 过 调 用 标 识 为
A N D R O I DA_L A R MG_ E T _T I M E 的 ioctl 设置 时间,也可 以通过标准 L inux 的 systemTime()
获取时间。

17.3 警报器 — 实时时钟 B SP 部分的结构

Android 警报器 系统 的 Java 层 、本地部分 的代码都是标准 的,不需要更改。内核 中的


Alarm 驱动程序 与硬件无关 ,在 Android 系统 中都是相 同的。因此 ,警报器系统 的移植实际
上就是 RTC 驱动程序 的移植 。
R T C驱动程序 也是 Linux 中一 种标准 的驱动程序 ,它在用户空间也提供 了设备节 点(自
定义的字符设备或 MISC 字符设备 )。根据 Android 系统 的 情况 ,不直接使用 RTC 驱动程
序,而是通过 Alarm 驱动程序 间接调用 RTC 系统 ,
而 Android 系统 的用户空间只调用 Alarm
驱动程序。

�17.3.1 RTC驱动程序
R T C 是 L inux 中标准 的实时时钟驱动程序框架 。驱动程序 的框架 内容在 内核代码 的
inlcude/linux/rtc.h 中定义 。代码路径在 内核 的 drivers/rte/ 目录 中。
以下两个函数用于注册和注销 RTC 设备 :
struct rtc_device *rtc_device_register(const c h a r *name, struct d e v i c e *dev,
const struct rtc_class_ops *ops,struct m o d u l e *owner);
v o i d rte卫eviq_E:_un 空 呾 上 空式 t :fu c t rtc_deyice rtcJ..;

注册的 rte_class_ ops 是 RTC 类 设备的操作 ,也是 RTC 驱动程序主要实现 的 内容 。


rte —class_ops 结构体定义如下所示 :
struct rtc_class_ops {
int (*open) (struct d e v i c e *);

293�
•oo Android 板级支持与硬件相关子系统

int ( * i o c t l ) ( s t r u c 七 d e v i c e * , unsigned i n t , unsigned long); //外部控制


int (*read_time) ( s t r u c t d e v i c e * , s t r u c t r t c _ t i m e * ) ; //读取时间
int (*set_time) (struct device *, s t r u c t rtc_time * ) ; //设置时间
int (*read_alarm) ( s t r u c t d e v i c e * , s t r u c t rtc_wkalrm * ) ; //读取警报
int (*set_alarm) ( s t r u c t device * , s t r u c t rtc_wkalrm * ) ; //设置警报
int (*proc) ( s t r u c t device * , s t r u c t s e q _ f i l e * ) ;
int (*set_mmss) ( s t r u c t d e v i c e * , u n s i g n e d l o n g s e c s ) ;
int (*irq_set_state) ( s t r u c t device * , i n t enabled);
int (*ir气_set_freq) (struct device *, i n t f r e q ) ;
int (*read_callback) (struct d e v i c e * , i n t data);
int (*alarm_irq_enable) ( s t r u c t d e v i c e * , unsigned i n t enabled);
int (*update_irq_enable) ( s t r u c t d e v i c e * , unsigned i n t enabled);
);

struct r t e _device 是对 st r u c t d e v i c e 的扩展 ,在 RT C 驱动程序 中使用 ,其 中也包含 了


r t e _c l a s s _ o p s 结构 。
RTC 驱动程序 的实现 实际上是实现 了 r t e_ c l a s s _ o p s 中的函数指针 ,主要包括 时间和警
报器这两个方面的内容。
在用户空间中,也可以通过 RT C 驱动程序 的设备节点对其进行调试 ,调试 的方法是通
过 i o c t l 命令 。R T C 命令号在 r tc .h 中定义 ,以 R T C 为开头。几个重要 的命令如下所示 :
#define RTC ALM SET ,I OW ( ' p ' , Ox07, s t r u c t r t e .time ) II设置警报器时间
#define RTC ALM READ IOR('p', Ox08, s t r u c t r t e time) / / 读取警报器时间
#define RTC RD TIME _IOR('p', Ox09, struct rtc_time) //读取RTC时间
ildefine RTC SET TIME IOW('p', OxOa, str旦ct_ r t c _ t i m e ) //设置RTC时间

�17.3.2 Alarm驱动程序
Android 系统没有直接在用户空间使用 RT C 设备 ,而是通过 内核空间的 A l r am 设备对
时间和警报器进行控制。
Alarm 驱动程序 为用户空间提供 的设备节点为/ d ev / a l a r m , 这是 一 个主设备号为 10 的
Misc 字符设备 ,其次设备号是动态生成的。
Alarm 驱 动 程 序 由 内核 代 码 中 如 v er s/ r tc / 目录 中 的 a l a r m.c 和 a l a n n - d ev .c 组成 。
include/ 血心 目录 中的 an d r o i d _ a l a r m . h 头文件提供 了到用户空间的各个 i o c t l 命令接 口。

一提示:在比较旧的版本的 A n d ro id 内核代码 中 ,比较 旧的版本 中只有 a l a r m.c 文件 。

Alarm 驱动程序 的实现基于 R T C 系统来实现 ,在其 r t e_ a l a r m _ a d d _ d e v i c e ( ) 函数 中,具


有如下调用:
err = rtc_irg_register(rtc 1 & a l a r l l ! _ r t � 一巨skJ;

这里的 al ar m_ r t e _task 是一 个 r t c _ t a sk 类型 的结构体 。


在 A l ar m 设备的 S u sp e n d 和 R e su m e 过程 中,也通过调用 RT C 的 r t e_ r e a d _ t i m e ( ) ,

rte_set_alarm() 等 函数进行 了操作 ,表示通过 RT C 系统存I 取 当前 的状态 。部分代码片段如


下所示:
r t e read t i m e ( a l a r m r t e dev, & r t e c u r r e n t r t e t i m e ) ; //获得时间
rtc_current_timespec.tv_nsec = O;
rtc_tm_to_time{&rtc_current_rtc_time,&rtc_current_timespec.tv sec);
save_time_delta {&rte卫elta, &rtc�urrent�imespec); //设置时间变化

�294
第 1 7 章 警 报 器 — 实时时钟系统 ooe

_
set_normalized_timespec(&elapsed_realtime_alarm_time,
a l a r m t i m e [ A N D R O I D ALARM ELAPSED REALTIME WAKEUP]
.tv_sec + elapsed_rtc_delta.tv_sec,
a l a r m t i m e [ A N D R O I D ALARM ELAPSED REALTIME WAKEUP]
. t v nsec + elapsed r t e d e l t a . t v n s e c ) ;

这里存储的信息内容大都基于静态的结构体,在ioctl的各个调用中,也通过操作这些
结构体完成。

17.4 警报器—实时时钟BSP部分的实现

汕 17.4.1 模拟器环境中的实现
模拟器的警报器 — 实时时钟部分实现 的特殊方面 ,只有模拟器 的 R T C 驱动程序 。这个
驱动程序在如vers/rtc/目录的rtc-goldfish.c文件中实现。GoldFish的实时时钟驱动由模拟器
的虚拟环境触发中断,并填充相关的寄存器,在驱动程序中取得信息。
goldfish_rte_read_timeO是其中读取时间的调用,内容如下所示:
s t a t i c i n t g o l d f i s h _ r t c _ r e a d _ t i m e ( s t r u c t d e v i c e *dev, s t r u c t r t c _ t i m e *tm) (
int64_t time;
struct goldfish_rtc *qrtc = platform_get_drvdata(to platform device(dev));
t i m e = r e a d l ( q r t c - > b a s e + TIMER_TIME_LOW); //读取虚拟的寄存器
豆 m e I = ( i n t 6 4 _ t ) r e a d l ( q r t c - > b a s e + TIMER_TIME_HIGH) << 3 2 ;
d o _ d i v ( 七 i m e , NSEC_PER_SEC);
rtc time to tm(time, tm);
r e t u r n 0;

以上的代码通过读取TIMER_T I M E _ L O W 和 T I M E R _ T I M E _ H I G H 这 两 个 虚 拟 寄 存 器 ,
获得当前时间。
对于模拟器的RTC驱动程序,goldfish_rte_ops是rte_class_ops类型的结构体,只实现
读取时间等部分功能。此外,也没有Suspend和Resume方面的处理。

�17.4.2 MSM平台和Nexus One系统的实现


Nexus One的警报器和实时时钟部分使用MSM平台的通用实现。
MSM平台实时时钟驱动程序的主要实现在drivers/rte的rtc-MSM7k00a.c文件中。具体
的 功 能 是 调 用 R P C (远程过程调用)完成的。
负责探测初始化的msmrtc_probe O函数如下所示:
s t a t i c i n t m s m r t c _ p r o b e ( s t r u c t p l a t f o r m _ d e v i c e *pdev) I
struct rpcsvr_platform_device *rdev =
container_of(pdev, s t r u c t rpcsvr_platform_device, base);
e p = msm r p c c o n n e c t ( r d e v - > p r o g , r d e v - > v e r s , 0 ) ; //连接到 RPC
I I 省略 部分错误处理的 内容
r七c = r t c _ d e v i c e _ r e g i s t e r { " m s m _ r t c " , & p d e v - > d e v , //建立 RTC 设备
& m s m _ r t c _ o p s , THIS_MODULE);
//省略部分错误处理的内容
r e t u r n O;

295
•oo Android 板级支持与硬件相关子系统

msm_rtc_ops 是 rte_class_ ops 类 型 的结构体 ,实现 了其 中的 read_time , set_ time 和


set_alarm 几个成员 。m sm rtc_timeremote _read_ t血 叨 函数负责读取 当前 的时间 ,其实现 的主
要部分如下所示:
s t a t i c i n t msmrtc_timeremote_read_time(struct d e v i c e *dev, s t r u c t r t c _ t i m e *tm) (
int re;
S七ruct t i m e r e m o t e _ g e t _ j u l i a n _ r e q { //表示RPC请求的结构体
s t r u c t rpc_request_hdr hdr;
uint32_t ju巨an_time_not_null;
J req;
struct timeremote_get_julian_rep { //表示RPC回应的结构体
struct rpc_reply_hdr hdr;
uint32一七 o p t _ a r g ;
s t r u c t rpc_巨me_ju让an time;
J rep;
r e q . j u l i a n _ t i m e _ n o t _ n u l l = cpu_to_be32 ( 1 ) ;
r e = m s m _ r p c _ c a l l _ r e p l y ( e p , TIMEREMOTE_PROCEEDURE_GET_JULIAN, / / RPC调用
&req, s i z e o f ( r e q } , &rep, s i z e o f ( r e p } , 5 * HZ);
//省略部分错误处理的内容
七m->tm_year = b e 3 2 _ t o _ c p u ( r e p . t i m e . y e a r ) ; //填充rtc_time结构
tm->tm_mon = b e 3 2 一 七 o _ c p u ( r e p . t i m e . m o n t h ) ;
tm->tm_mday = b e 3 2 _ t o _ c p u ( r e p . t i m e . d a y ) ;
tm->tm_hour = be32_to_cpu(rep.time.hour);
tm->tm_min = b e 3 2 _ t o _ c p u ( r e p . t i m e . m i n u t e ) ;
tm->tm_sec = be32_to_cpu(rep.time.second);
t m - > t m wday = b e 3 2 t o c p u ( r e p . t i m e . d a y _ o f _ w e e k ) ;
川省略部分错误处理的内容
tm->tm_year - = 1900; II设赏从1900开始的RTC
tm->tm mon--; II RTC月份的调整
//省略部分错误处理的内容
r e t u r n 0;

具体的实现是通过统 一 的 R PC 在远端完成 的,这里调用 的是 RPC 的 T IM E R E M O T E


P R O C E E D U R E _ G E T _JULIAN 命令 。
msmrtc _ suspend() 实现这个驱动程序模块 的挂起 (Suspend ) 函数 ,内容如下所示 :
s t a t i c i n 七 m s m r t c _ s u s p e n d ( s t r u c t p l a 七 f o r m _ d e v i c e * d e v , pm_message_t s t a t e ) {
i f (rtcalarm一七ime) {
u n s i g n e d l o n g now = m s m r t c _ g e t _ s e c o n d s ( ) ;
i n t d i f f = r t c a l a r m _ t i m e - now;
i f ( d i f f <= 0) { //判断时间范围
msmr七c_alarmtimer_expired(l); I I调用内部的函数
msm_pm_set_max_sleep_time(O);
r e t u r n 0;

msm pm s e t max s l e e p t i m e ( ( i n 七 6 4 t ) ((int64 t) diff * NSEC PER S E C ) ) ;


} else
msm pm s e t max s l e e p 七 i m e ( O ) ;
r e t u r n O;

这里进行的处理就是根据所设置的警报器时间,得到距离系统现在时间的差值,然后
调用 PM (电源管理)的 m sm _pm_ set_ max_ sleep_ tim 叨 函数把数值 设置为睡眠 的时间。这
样,在时间到达的时候,可以进行 PM 系统 的唤醒 。

296
第17章警报器—实时时钟系统 ooe

迪 17.4.3 NexusS 系统的实现

1. 电源管理芯片公用部分

Nexus S 系 统 的 R T C 部 分 主 要 的 硬 件 是 M 心 ( 8 9 9 8 电 源 管 理 芯 片 , M A X . 8 9 9 8 通 过 l 2 C
连接到处理器上。在 sys 文 件 系 统 的 目录 中 , 可 以 查 看 到 这 个 设 备 , 如 下 所 示 :
ii ls -1 /sys/bus/i2c/devices/6-0066/

6-0066 就 是 M A X 89 9 8 在 l2 C 总 线 上 的 地 址 , m ax 89 9 8-p m ic 和 m ax 89 9 8-ch arg er 是 其



中的两个设备。这是 个 多 功 能 杂 项 设 备 (M F C , Multifunction Miscellaneous Devices)。
其驱动程序为如vers/mfd/max8998.c文件,相关的头文件定义则在max8998-private.h文件
中,max8998.c中相关的定义如下所示:
static struct mfd cell max8998 devs[] = {
{. name = "max8998-pmic",), {. name = "max8998-charger",)
);

这 两 个 内 容 被 调 用 m f d _ a d d _d e v i c e s O 注 册 到 了 M F D 系 统 , 实 现 的 内 容 是 将 l 2 C 部 分
封 装 读 、 写 函 数 。 此 处 实 现 的 内 容 也 被 电 池 部 分 ( 如 v e r s / p o w e r / s 5 p c 110_ battery.c)所调用。

2. R T C 及 其 调 用 者

Nexus S系统的RTC驱动程序本身涉及如vers/rtc/目录中的几个文件,rtc-s3c.*是典型
的 R T C 部 分 驱 动 , 驱 动 的 名 称 为 " s 3 c - r t c " o rtc-max8998.c当中实现了rtc-s3c.h中定义了
m a x 8 9 9 8 _rte_set_t i m e ( ) 和 m a x 8 9 9 8_ rtc_read _ t i m e ( ) 两 个 函 数 , 它 们 分 别 是 R T C 驱 动 程 序 调
用M心郊998电源管理芯片的接口。
由于需要使用l2C部分,rtc-max8998.c中定义了i2c_driver结构,并对其进行了注册,
而其中设置RTC时间的函数如下所示:
int max8998_rtc_set_time(struct rtc_time *tm) {
max8998 dbg ("%s %02d. 号02d.%02d %02d/%02d/号02d\n", func ,
tm->tm_year, tm->tm_mon, tm->tm_mday,
七m->tm_hour, tm->tm_min, tm->tm_sec);
return max8998 rte i2c set time(max8998 rte i2c client, tm);I I ' 从 i 2 c 进行 设置

r t c - s 3 c . c 中 定 义 R T C 驱 动 的 核 心 r t e _class_o p s 结 构 如 下 所 示 :

static const struct rtc_class_ops s3c_rtcops = {


.open = s3c rte open,
.release = s3c rte release,
.ioctl = s3c rte ioctl, //端口控制
.read time = s3c. rte get七ime,
,. //获取时间
.set巨me = s3c rte set七ime, //设置时间
.read alarm = s3c rte getalarm, //获得警报器信息
.set,alarm
. = s3c rte setalarm, //设置警报器
.irq set freq = s3c rte set freq,
.irq__set_state = s3c_rtc_setpie,
.proc . proc,
= s3c .rte
};

297
• O O Android板级支持与硬件相关子系统


此处的RTC驱动中对实际RTC硬件的主要调用有两处, 处是在初始化探测阶段获

取时间,另 处是在 时间设置方面 。
其探测函数s3 c_ rte_pro be()中调用max8998_ rtc_read_time()函数读取了时间,基本结构
如下所示:
static int _ d e v i n i t s3c_rtc_probe(struct platforrn_device *pdev) {
s七ruct r七c_device *rte;
struct resource *res;
u n s i g n e d char bcd_tmp, bcd_loop;
int ret;
struct rtc_time tm; //表示时间的结构
int year;
II省略对硬件的初始化控制和数据结构的处理
s 3 c _ r t c _ c p u 卫 y p e = p l a t f o r m _ g e t _ d e v i c e id(pdev)->driver_data;
max8998 rte r e a d time(&tm); II获取时间
//省略后面从 PM IC 芯片更新时间
s3c rte enable (&pdev->dev, 1) ; I I 使能 R TC
writeb(bin2bcd(七m.tm_sec), s3c rte b a s e + S3C2410 RTCSEC); II 寄存器设置
writeb(bin2bcd(tm.tm_min), s3c_rtc_base + S3C2410_RTCMIN);
writeb(bin2bcd(tm.tm_hour), s3c_rtc_base + S3C2410_RTCHOUR);
writeb(bin2bcd(tm.tm_mday), s3c_rtc_base + S3C2410_RTCDATE);
wri七eb(bin2bcd(tm.tm_mon + 1), s3c_rtc_base + S3C2410_RTCMON);
s3c r七c enable(&pdev->dev, 0); II 禁止 RTC
r七c = rte d e v i c e register ("s3c'勹&pdev->dev, &s3c_rtcops,THIS_MODULE);
II 省略部分 内容
return 0;
//省略错误处理

另一 个对 R T C 硬件 的调用是在 rte_class_ops的set_time进行时间的设置,调用的就是
max8998 _rte_set_time()函数,核心的内容如下所示:
sta七ic int s3c_rtc_settime(struct d e v i c e *dev, struct r七c_time *七m) {
void iomem *base = s3c;_rtc_base;
II省略部分内容
w r i t e b ( b i n 2 b c d ( t m - > t m sec), b a s e + S3C2410 RTCSEC); II 直接写相关的寄存器
writeb(bin2bcd(七m->tm_min), b a s e + S3C2410_RTCMIN);
writeb(bin2bcd(tm->tm_hour), b a s e + S3C2410_RTCHOUR);
writeb(bin2bcd(tm->tm_mday), b a s e + S3C2410_RTCDATE);
w r i t e b ( b i n 2 b c d ( t m - > t m m o n + 1), b a s e + S3C2410_RTCMON);
//省略部分内容:设置 R T C 的年寄存器
s3c_rtc_enable (dev, 0); I I 对 RT C 进行使 能
m a x 8 9 9 8 _ r t c _ s e t _ t i m e (tm); I I 调 用 M A X 8 9 98 的功能 ,进而 调用 m a x 8 9 98_ r t c_ i2 c_ w r ite
return 0;

在实现中除了直接写RTC部分的寄存器之外,还调用M心郊998在I2C系统上进行了
相应的时间设置。

逵17 .4.4 Galaxy Nexus系统的实现


Galaxy N e x u s 系 统 的 警 报 器 和 实 时 时 钟 部 分 的 实 现 只 和 O M 凡> J:JZ台的 T W L 6040 模块
有关,虽然这是处理器之外的芯片,但是也由TI提供,因此在 一 般 的 O M A I卢 F 台中都是

�298
第 1 7 章 警 报 器 — 实时时钟系统 oo•

这 样 的 。 T W L 6 0 4 0 通 过 1 2 C 与 O M 凡攷处理器相连 。
OMAP中的TWL部分RTC的实现是内核代码的drivers/rtc/rtc-twl.c文件,另 一 个相关
的文件是drivers/mfd/twl-core.c o rtc-twl.c中所定义的平台驱动的名称为"twl_rtc"。
rtc-twl.c中的时间读取的实现如下所示:
s t a t i c i n t twl_rtc_read_alarm(struct d e v i c e *dev, s t r u c t r t c _ w k a l r m *alm) {
u n s i g n e d c h a r rtc_data[ALL_TIME_REGS + 1 ] ;
int ret;
r e t = t w l i 2 c read(TWL MODULE RTC, r t c _ d a t a , //通过I2C读取RTC的数据
(rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
II省略部分内容
alrn->七irne.tm_sec = bcd2bin(rtc_da七a [OJ}; //获取时间,用BCD码坟充数据结构
alrn->tirne.trn_rnin = bcd2bin(rtc_data[l]);
alrn->tirne.trn_hour = bcd2bin(rtc_data[2]);
alm->time.tm_rnday = bcd2bin(rtc_data[3]};
alrn->tirne.trn_rnon = bcd2bin(rtc_data[4]) - l;
alm->tirne.trn_year = bcd2bin(工tc_data[S]} + 100;
i f (r七c i r q b i t s & B I T RTC INTERRUPTS REG I T ALARM M) a l m - > e n a b l e d = l ;
return ret;

其中调用的twl_i2c_read()就来自于TWL6040芯片的核心实现部分,在这个函数本身
的实现正是通过l2C进行的操作。
T W L 6 0 4 0 所 对 应 的 设 备 连 接 到 l 2 C 第 一 个控制器 的 总线上 ,地址为 1-0048, 可以从
sys文件系统中看到其内容,如下所示:
shell@android:/ $ c a t /sys/bus/i2c/dev飞ces/1 二0 0 4 8 / n arn e
twl6030

299�
第18章
光系统

18.1 光系统概述

背光和指示灯系统部分在Android中作为光系统统 实现 。A ndroid 的光系统 负责控制
系统中的各个光源,例如屏幕背光、键盘按键光、电池光等。光系统基本上是 一 个用于输
出控制的系统。
光系统从驱动程序、硬件抽象层、本地框架到Java层都具有内容,但是没有直接向应
用程序层提供直接的API。
Android背光和指示灯的相关内容如表18-1所示。

表 18-1 光系统的相关内容
Android 的层次 光系统部分 描 述
硬件层次 背光、指示灯等光源硬件
操作系统层 LED, 背光或其他驱动程序 输出型驱动程序,结构比较简单
本地的硬件抽象层 lights 硬件模块 需要对光源进行处理
本地框架层 系统服务器中的JN[ 被系统服务器的 Java 部分调用
Java 框架层 LigbtsService 在 com android.server 当中,属于系统服务器
Java 层的 A P ! 无直接同框架层的 A P I 仅有被其他系统调用的部分

18.2 背光和指示灯子系统的结构

�18.2.1 总体结构
Android光系统自下而上包含了驱动程序、光系统硬件抽象层、光系统Java框架类、
Java框架中光系统使用等几个部分,其结构如图18-1所示。
第18章光系统 ooe

• 厂已芦
一一一尘玉兰--------------------- :o:二:rvice
Lightslervice
--------------------------------------
LigbtsService JNI

------------------------··
,--------···---------
: Lights Hardware Module
-------------··----------------
本地框架:
Linux内核层 i
·---------一一一一一一一一一一一一
Lights 驱动
!:
j :
----------------------------------------
移植部分

图 18- 1 Android 的光系统结构

自下而上,光系统包含了以下内容。
(1)驱动程序:特定硬件平台光系统的驱动程序
作为光源部分的控制,一般基于 L inux 的 L E D 驱动程序实现 ,使用 sys 文件 系统作 为
对用户空间的接口。背光方面还具有单独的驱动框架。
(2)硬件抽象层
• hardware/libhardware/include/hardware/lights.h
光系统是 A ndroid 中标准 的硬件模块 ,实现后将 生成名称 为 lights.<hw>. so 的动态库 ,
放置在目标文件系统 /system /lib/hw /目录 中。
(3) JNI部分
• frameworks/base/services/jni/com _android_ server_ LightsService.cpp
这个文件是光系统部分的本地代码,同时提供了JNI的接口。
(4) Java 部分 :框架层 的服 务
• frameworks/base/services/java/com/android/server/LightsService.java
com.android.server 包 中的 L ightsService 类通过调用 JNI 来实现 。光系统部分没有直接

的 Java 的 A PL 但 是 A ndroid 系统 的 Java 服务库和其他 些部分对光 系统有所调用 。

�18.2.2 JNI部分
com_ android_ server_ LightsService.cpp 是 A ndroid 中光系统 的本地代码兼 JN I 部分 。与
其他部分的调用相比,此处需要处理多个光源设备的情况。
这个文件提供了 com .android.server 包 中的 L ightsService 类 的本地代码部分 ,其 中定义
的方法列表如下所示:
static JNINativeMethod method_table[J = {
{ "init_native", "(JI", ( v o i d * ) i n i t _ n a t i v e ) ,
("finalize_native", "(I} V", (void*} f i n a l i z e _ n a t i v e ) ,
{ "setLight_native", " (IIIIIII) V", (void*) setLight_na七ive },
);

其中最为重要的就是 setL ight_native 功能的实现 ,这个 函数具有 一 个 int 类 型的参数 ,

301�
•oO Android板级支持与硬件相关子系统

表示某个光源的ID。
Devices结构体实际上是 一 个 light—
device—
t类型指针 的数据 ,内容如下所示 :
s t r u c t Devices {
l i g h t _ d e v i c e _ t * lights[LIGHT_COUNT]; //默认为LIGHT_ID_的数目
);

对于光系统,由于light_device_t并没有 一 个表示设备 ID 的成 员 ,因此 区分设备的方


式是通过向光设备模块的打开函数中传递设备ID (LIGHT_ID_)作为参数。这部分在
get_device()函数中实现,如下所示:
s t a t i c l i g h t _ d e v i c e _ t * g e t _ d e v i c e ( h w _ m o d u l e _ t * m o d u l e , c h a r c o n s t * name) {
int err;
hw d e v i c e t * d e v i c e ;
e r r = m o d u l e - > m e t h o d s 今 op e n ( m o d u l e , name, & d e v i c e ) ; //打开模块,传递参数
if ( e r r = = 0) {
return (light_device_t*)device; //设置返回指针
} e l s e { r e t u r n NULL; }

这里传入的参数name, 就是向模块传递的参数,返回 一 个 light_device_t类型的指针。


init_native()的实现中调用get_device()获得各个设备,初始化了Devices中的lights数组。
setLight_native()函数用于设置源的参数,实现方式如下所示:
static v o i d setLigh七_native(JNIEnv *env, j o b j e c t clazz, i n t p t r ,
i n t l i g h t , i n t c o l o r A R G B , i n t f l a s h M o d e , i n t onMS, i n t o f f M S , i n t b r i g h t n e s s M o d e )

Devices* devices = (Devices*)ptr;


light_s七ate_t state;
//省略部分错误处理内容,如果devices 今 l i ght s [ l i g h t ] 为 N U L L , 直接返回
memset(&state, 0, s i z e o f ( l i g h 七 _ s t a t e _ t ) ) ;
s t a t e . c o l o r = colorARGB;
state.flashMode = flashMode;
s t a t e . f l a s h O n M S = onMS;
state.flashOffMS = offMS;
s t a t e . b r i g h t n e s s M o d e = brigh七nessMode;
devices->lights[light]->set_light(devices->lights[light], &state);

此处调用的set_light就是硬件抽象层提供的统 一 的光源控制接 口。

18.2.3 Java服务部分和调用部分
LightsService.java实现光系统的Java类LightsService, 它运行千系统服务器进程,这
个类不向Java应用程序层提供API。与系统服务中的大多数服务不同,LightsService类不
是 一 个基千 B inder 的实现 ,也不提供 IPC 的远程接 口。
系统服务器中的其他服务如PowerManagerService, N otificationManagerService对光系
统的LightsService做出了调用。其中涉及LCD背光、电池光、按钮指示光、键盘光等多个
方面,它们提供接口供Java应用程序层调用。因此光系统并没有直接对Java应用层的APL
只有间接的接口。

302
第18章光系统 ooe

18.3 背光和指示灯BSP部分的结构

从移植的角度,光系统包含了硬件抽象层和驱动程序两方面的内容。
光系统本身只有简单的控制功能,因此其驱动程序的实现也比较简单。在Linux中,
LED驱动程序框架也比较适合作为光系统驱动程序。LED驱动程序提供给用户空间的接口
是sys文件系统。

光系统的硬件抽象层是Android中 个 标 准 的 硬 件 模 块 , 底 层 和 A n d ro id 系 统 本 地 部
分的接口,这部分内容实现的功能是控制各个光源的状态。

汕 18.3.1 驱动程序
Android光系统的驱动比较简单,可以使用字符设备的文件操作file_operation, sys文
件系统等。

1. LED 驱 动
在Linux中,LED驱动程序框架比较适合作为光系统,其头文件在linclude/linux/leds.h
目录中定义。在Linux内核配置中,打开NEW_LEDS和LEDS—CLASS可以获得基本的
LED驱动支持,在菜单配置的Device Drivers选项中。如vers/leds/目录中的led-class.c、
led-core.c提供了LED驱动核心部分的实现。
led_ c l a s s d e v 结 构 体 表 示 的 是 L E D 设 备 , 是 L E D 驱 动 程 序 的 核 心 , 其 内 容 如 下 所 示 :
struct led_classdev {
const char *name; //设备名称
int brightness; //亮度
int flags;
#define L E D _ S U S P E N D E D (1«0)
itdefine L E D C O R E S U S P E N D R E S U M E (1 << 16)
II设置亮度级别
void (*brightness_set) (struct led_classdev *led_cdev,
enum led_brightness brightness);
//获得亮度级别
enum led brightness (*brightness_get} (struct led_classdev *led_cdev};
//激活硬件加速的闪烁
int (*blink_set} (struct led_classdev *led:-cdev,
unsigned long *delay_on, unsigned long *delay_off};
struct device *dev;
struct list head node; II L E D 设 备 链 表
//省略部分内容
);

led_ c l a s s d e v 使 用 以 下 两 个 函 数 进 行 注 册 和 注 销 :
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
void led_classdev__unregis七er (struct led_classdev *led_cdev归

当实现了 个 led_ c l a s s d e v 并 注 册 后 , 系 统 将 出 现 一 个 L E D 设 备 , 同 时 在 sy s 文 件 系
统的/sys/class/Ie d s / 目 录 中 , 将 出 现 一 个 以 led_ c l a s s d e v 设 备 名 称 ( n a m e ) 成 员 为 名 称 的 子

303�
• O O Android 板级支持与硬件相关子系统


目录。在每个设备子目录中将出现 个 brightness 文件 ,读写这个 文件可 以获得和设置 LED
设备的亮度。
对于 LE D 驱动程序 ,如果需要调试 ,直接读写 sys 文件 系统 中相应设备的 brightness
文件即可。

2. 背光驱动
也有某些硬件屏幕的背光并不是由 LED 硬件提供 的,因此 Linux 当中还有 一 个和背光
(bac 灿 ght) 相关的架构 ,背光的架构又通常和 Fram ebuffer 驱动有关系 。
背光驱动的头文件路径为: include/linux/backlight.h 。
背光部分的实现在 drivers/video/bac灿 ght/目录 中,bac汕 ght.c 文件是背光的核心部分实
现, generic_bl.c 作为通用 的实现 。
backlight.h 定 义 了表 示背 光操 作 的 bac灿 ght_ops 结构 和表 示背 光 设 备 的 bac灿 ght_
device 结构 ,如下所示 :
struct backlight_ops {
unsigned i n t options;
# d e f i n e BL CORE SUSPENDRESUME (1«0)
i n t (*update_status) ( s t r u c t backlight_device * ) ; //设置背光
int (*get_brightness) (struct backlight_device *); //获取背光的亮度
i n t (*check_fb) ( s t r u c t f b _ i n f o * ) ; //检查 Fr arneb u f f e r 驱动
):
struct backlight_device {
s t r u c t backlight_properties props; //设赏的属性
s t r u c t mutex update_lock;
s t r u c t mutex o p s _ l o c k ;
s t r u c t backlight_ops *ops; //背光的操作
struct notifier block fb notif;
s t r u c t device dev;
):

另外的两个函数提供了背光注册和注销:
e x t e r n s t r u c t b a c k l i g h t _ d e v i c e * b a c k l i g h t _ d e v i c e _ r e g i s t e r ( c o n s t c h a r *name,
s t r u c t device *dev, v o i d *devdata, s t r u c t backlight_ops *ops);
e x t e r n . 又 o i d b a c k l i g 贴 _啦 �i c e_ u n r e g i s t e r ( s t r u c t q J 起 K 垃 , g h L Q e v i c e _ *达 U. ;

当一 个背光设备实现 并注册之后 ,将 根据名称生成/sys/class/back.light/<nam e> 目录 ,


其中 brightness 文件 的读写用于控制背光 的亮度 。
背光部分本身的控制基本独立,但是需要和 Fram ebuffer 交互 。既然 Fram ebuffer 驱动
是主要的设备节点,因此 Fram ebuffer 发生事件 的时候 ,也要调用背光部分 的回调 函数 。这
也是背光核心部分 bac灿 ght.c 文件 中处理 的 内容 。

18.3.2 硬件抽象层的内容

1. 硬件抽象层的接口
光系统的硬件抽象层在 lights.h 文件 中定义 ,这是标准 的 A ndroid 硬件模块之 一 。
light_state_t 结构体表示 一 个光设备的状态 ,内容如下所示 :

�304
第18章光系统 ooe

struct light state t (


u n s i g n e d int color; //光源的颜色
int flashMode; // F l a s h 的模式 、开关时间
int flashOnMS;
int flashOffMS;
int brightnessMode; //亮度模式
);

在lights.h文件中,以LIGHT_ID_开头的字符串表示各种光源的名称,包括背光
("backlight")、键盘("keyboard")等内容。
光设备的结构体为light_ device_ t, 内容如下所示:
struct light d e v i c e t {
struct h w d e v i c e t common;
int (*set light) (struct l i g h 七 d e v i c e t* dev, I I 设 为光源
s七ruct l i g h 七 s t a t e t const* s七ate);
);

light_ device_ t中只有 一 个 set_light 函数指针 ,以 light_state_t类型 的指针 为参数 ,表示


设置光源的状态。

2. 实现光系统的硬件抽象层
光系统的硬件抽象层的实现比较简单,只要实现了相应的设置接口即可。相对于
Android中的其他硬件模块,光系统的主要特点是需要为每 一
个光源 实现一 个 设 备
light_ device _t。
光系统模块的打开函数,也就是Android中标准打开模块的函数(hw _module_methods_t
“ ”
中的open)需要通过参数 返回 一 个 light_device _t类型的指针。这个指针表示的是 一 个
光设备,在模块的打开函数中根据指定的名称来确定得到哪 一 个设备。
对于没有的光设备,不需要桩实现,将返回的light_device_t指针设置为NULL即可,
上层代码会对此进行处理。

18.4 背光和指示灯BSP部分的实现

�18.4.1 Nexus One 系统的实现

1. 驱动程序
Nexus One系统的实现中,具有儿个不同的发光源,这些光源的硬件各不相同。但是
统 一 实现 了几个 led_classdev设备。
背光设备是其中最重要的 一 个 设备 ,在 M SM 内核代码 arch/arm /m acb-m sm /目录 的
board-mah 血 abi-panel.c 文件 中定义 ,内容如下所示 :
s七atic struct led_classdev mahimahi_brigh七ness_led = {
.name = "lcd-backlight",
. b r i g h t n e s s = LED_FULL,
.brightness s e t = m a h i m a h i b r i g h t n e s s set,

};

305�
•oo Android 板级支持与硬件相关子系统

这里的 "lcd -b ack lig h t" 为光设备 的名称 ,m a h im a h i_ brightness—set 为设置光亮度 的函数 ,
led_ classdev 中其他 的函数指针没有实现 。这个驱动程序将在 sy s 文件 系统 的Isys! class/leds/

目录中生成 个名称 为 lcd -b ack lig h t 的子 目录 。

2. 硬件抽象层
MSM 平 台的光系统 的硬件抽象层 的代码路径 为:hardware/msm7k/1iblights, Nexus O n e
系统的板级支持的路径为: device/htc/passion-common/liblights, 其中的源文件名称都是
lights.c, 二者实现的内容基本类似。 N ex u s O n e 系统 中使用后者生成 的 lig h ts.m ah im a h i.so 。
lights.c 中模块打开函数 的实现如下所示 :
S七atic int open_lights(const struct hw_module_t* module, char const* name,
struct hw_device_七** device) {
int (*set_light) (struct light_device_t* dev, //定义 一 个函数指针
struct light state t cons七* state);
//省略部分内容:根据设备的名称为set light函数指针赋值
pthread_once(&g_init, init_globals);
struct light_device_t *dev = rnalloc(sizeof(struct light_device_七));
rnernset(dev, 0, sizeof(*dev));

dev->common. 七ag = HARDWARE_DEVICE_TAG;


dev->common.version = 0;
dev->common.module = {struc七hw_module_t*)module;
dev->common.close = (in七{*) {struct hw device t*))close lights;
dev->set_light = set_light; //这里设置的函数指针,根据名称不同
*device = {struct hw_device_t*)dev;
return O;

o p e n _lights 将作 为模块打 开 函数 ( h w _ m o d u l e _ m e t h o d s _ t:: o p e n ) 使用 ,根据 不 同的设



备返回 lig h t_ device_ t 类型的函数指针 ,其 中唯 的区别就是 se t—lig h t 成员被设置成不 同的
函数。
背光设备的 set_ lig h t 实现为 set_ lig h t_ backlight() 函数 ,其 实现如下所示 :
char const*const LCD_FILE = "/sys/class/leds/lcd-backlight/brightness";
static in七set_light_backlight(struct light_device_t* dev,
struct light_state_t const* state) (
int e r r = 0;
in七brightness = rgb to brightness s t a t e)//将RGB数值转化为亮度
;
pthread_mutex_lock(&g_lock);
g_backlight = brightness;
err = write int (LCD FILE, brightness); //将数值写入sys文件系统的brightness文件
II passio 忙 c om mo n 中的实现 le d s 数组中的主要信息也是路径:
/ / e r r = w工 (&led s [LC D_ BAC K L IG HT ) .brightness, brightness);
pthread_rnutex_unlock(&g_lock);
return err;

LCD_FILE 宏定义为 sy s 文件 系统 中可 以控制设备文件 的路径 ,led s 数组 中的信息与之


类似。由于 lig h t_ state_ t 中的参数 为 c o lo r, 表示 32 位的 A RG B 数据 ,而背光硬件只支待亮
度,因此使用 rg b_t o _ brightness() 函数将 RG B 转化为亮度 的整数值 。
在此硬件抽象层中还包括键盘、按键、电池等内容,实现方式与背光类似:均通过写

306
第18章光系统 ooe

sys文件系统的!eds类型的设备完成,除了控制亮度的brightness文件之外,还可能有表示
闪动的blink文件。

迪 18.4.2 Nexus S 系统 的实现

1. 驱动程序
Nexus S系统的背光是 一 个使用 SP I 总线连接 的设备 ,因此具有专 门的背光驱动程序 。
由于背光与显示屏的Framebuffer设备有关,因此其代码在drivers/video/samsung/目录中,
文件为s3cfb_tl2796.c。
可以通过sys文件系统访问背光设备,写入和读取的方法如下所示:
41 e c h o " 1 2 7 " > / s y s / c l a s s / b a c k l i g h t / s S p _ b l / b r i g h t n e s s
i cat /sys/class/backlight/sSp_bl/brightness
127

同目录中还有actual_brightness和max_brightness两个文件,它们分别表示实际的亮度
和最大亮度。
驱动实现的探测函数tl2796_probe()中,具有如下的代码:
lcd->bl_dev = backlight_device_register("s5p_bl",
& s p i - > d e v , l e d ! &s5p_bl_Of)S, NULL);

这其中将背光设备设置到了Framebuffer设备当中。背光的操作由bac灿ght_ops类型
的s5p_bl_ops结构定义,内容如下所示:
c o n s t s t r u c t b a c k l i g h t _ o p s sSp_bl_ops = {
.update_status = sSp_bl_update_status, //背光更新
) ;

s5p—bl_update_status()函数的主体如下所示:
s t a t i c · i n t s 5 p _ b l _ u p d a t e _ s t a t u s ( s t r u c t backligh七_device *bd) {
struc七s5p_lcd *led= bl_get_data(bd);
int bl= bd->props.brigh七ness;
if (bl< 0 I I bl> 255) r e t u r n -EINVAL; //控制背光数值的设置
mutex_lock(&lcd->lock); //加锁,避免同时操作
lcd->bl = bl;
i f (lcd->ldi_enable) { update_brightness (led); II更新背光设备
mutex_unlock(&lcd->lock); //解锁
r e t u r n 0;

如果在用户空间写sys文件系统中的brightness, s5p_ bl_update_status()函数将会被调用,


为了避免同时写,需要加锁操作。

2. 硬件抽象层
Nexus S光系统的硬件抽象层为板级别的特定代码,其中实际上包含了对背光的控制,
代码路径为: device/samsung/crespo/liblight/, 生成名称为lights.s5pcl 1O.so的动态库。
与一 般 的光模块 的硬件抽象层相 比,此驱动 的实现只是背光部分 ,没有包括指示灯部
分,其中定义的背光设备的路径为:

307
• O o Android板级支持与硬件相关子系统

char const *const_I, D一旦i上且;' :.:但ys/c上免£lS/b色c:klight/s


. fl_!?l/brightness";

硬 件 抽 象 层 中 用 于 实 现 l i g h t _device_t结构中的set_light函数指针的set_light_backlight()
函数如下所示:
static int set_light_backlight(struct light_device_t *dev,
struct light_state_七canst *state) {
int err = 0;
int b r i g h t n e s s = rgb_to_brightness(state); / / 将 R G B 转换成亮度
pthread_mutex_lock(&g_lock);
err = write_int (LCD_FILE, brightness); //写背光控制文件
pthread_mutex_unlock(&g_lock);
return err;


bac汕gbt背光的驱动设备的控制和LED设备完全相同,写入 个 0 255 的 整 数 值 表
示输出的亮度。

�18.4.3 Galaxy Nexus系统的实现



Galaxy N e x u s 系 统 的 背 光 和 两 个 方 面 有 关 系 : 个 是 东 芝 (P an aso n ic ) 的 L E D 控 制

芯片AN30259A, 另 个 是 三 星 的 S 6 E 8A A O 屏 。 由 于 硬 件 的 发 展 ,G alax y N e x u s 的 背 光 已
经不是LED的形式,而是包含在LCD控制器的功能中。

1. LED 驱动程序

用于控制LED的AN30259A芯片的驱动与 般 的 L E D 实 现 接 口方 式 不 同 ,不 使 用 L E D
的 驱 动 结 构 , 而 使 用 M I S C 字 符 设 备 的 接 口 , 设 备 节 点 为 / d e v / a n 3 0 2 5 9 a_leds。
A N 3 0 2 5 9 A 驱 动 程 序 的 代 码 路 径 为 : drivers/misc/leds-an30259a.c。
A N 3 0 2 5 9 A 的 头 文 件 为 : include/linux/leds-an30259a.b。
本 设 备 的 f i l e _o p e r a t i o n s 中 只 有 i o c t l 作 为 用 户 空 间 的 控 制 接 口 , 各 个 数 值 在
leds-an30259a.b头文件中定义:
static long an30250a_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
struct an30259a_data *leds_data = container_of(file->private_data,
struct an30259a_data, dev);
struct i2c_client * c l i e n t = leds_data->client;
int retval, 1.; u8 imax;
struct an30259a 卫 r_ control leds[3];
mutex_lock{&leds_data->mutex);
switch (cmd) {
case AN30259A PR SET LED: //设登 LED
retval = copy_from_user(leds, (unsigned char _ u s e r *)arg,
sizeof(struct an30259a_pr_control));
II 省略错 误处理部分 内容
for (i = LED_R; i <= LED_B; i++) {
retval = leds_handle_cmds(client, i, leds);
if (retval < 0) goto an30259a_ioctl_failed;

re七val = leds i2c write all(client); //通过 i2c 进行控制


break;
II 省略其他命令

�308
第18章光系统 oo•

an30259a实际上也是连接在I2C总线的硬件,地址为4-0030, 在I2C的sys文件系统
中的/sys/bus/i2c/devices/4-003 0有其信息。
2. LCD背光驱动程序
S 6 E 8 A A O 屏 是 O M 凡》处理器显示系统的外接 LC D 设备,其驱动程序和 Fram ebuffer
密切相关,其代码路径为: drivers/video/omap2/displays/panel-s6e8aa0.c。
背光是panel-s6e8aa0.c中实现的主要部分之 一 ,相关代码如下所示:
sta七ic int s 6 e 8 a a 0 _ s e t _ b r i g h t n e s s ( s t r u c t b a c k l i g h t _ d e v i c e *bd) {
struct o m a p _ d s s _ d e v i c e *dssdev = dev_get_drvdata(&bd->dev);
struct s6e8aaO_data *s6 = dev_get_drvdata(&dssdev->dev);
int b l = bd->props.brightness;
int ret = 0;
if ( b l = = s6->bl) return 0;
s6->bl = bl; //从背光设备中取出亮度
mutex_lock(&s6->lock);
if (dssdev->state = = O M A P DSS DISPLAY ACTIVE) {
dsi bus lock(dssdev);
r e 七 = s6e8aaO_update_brightness(dssdev); //更新背光操作
dsi b u s unlock(dssdev);

mutex_unlock(&s6->lock);
return ret;

static in七s6e8aa0_get_brigh七ness(struct b a c k l i g h t d e v i c e *bd) (
re七urn bd->props.brightness; //从内部得到

static const struct b a c k l i g h t _ o p s s6e8aaO_backlight_ops = (
.get_brightness = s6e8aa0_get_brightness,
.update_status = s6e8aaO_set_brightness,
);

在以上的实现中,背光的设置部分是通过对硬件操作完成的,而读取部分则只是从驱
动保存的数值中得到。s6e8aa0_update_brightness()函数中调用s6e8aa0特定的写函数,完成
了硬件设置操作。在驱动的probe()函数中,把s6e8aa0_bac汕ght_ops注册为名称"s6e8aa0"
的背光设备。
在用户空间中,/sys/class/bac灿ght/s6e8aa0/目录是s6e8aa0的背光设备的目录,其中主
要用brightness完成设置和读取操作。
3. 硬件抽象层
Galaxy Nexus背光的硬件抽象层就是tuna板的实现,支持了LCD的背光和两个LED
光源。其代码路径为: device/samsung/tuna/liblight/, 其中的内容将生成lights. tuna.so, 放置
在目标系统的/system/lib/hw目录中。
LED通过字符设备操作,LCD通过bac灿ght设备操作,主要的代码部分如下所示:
char c o n s t *const L C D F I L E = "/sys/class/backlight/s6eBaa0/brightness";
char const * c o n s 七 L E D _ F I L E = "/dev/an30259a_leds";
s t a 豆 c int w r i t e _ l e d s ( s t r u c t an30259a_pr_control *led) (
int e r r = 0; int imax = IMAX; in七fd;
p t h r e a d m u t e x lock(&g lock);
fd = open (LED_FILE, O_RDWR); / / 打开 S 6E 8A A O 屏 的字符 设备文件

309
•oo Android板级支持与硬件相关子系统

if ( f d >= 0) {
e r r = i o c t l ( f d , AN30259A PR SET IMAX, & i m a x ) ; // ioc七1 硬件操作
if (err) ALOGE("failed t o set imax");
e r r = i o c t l ( f d , AN30259A PR ,SET .
LED, l e d ) ; // i o c t l 硬件操作
if ( e r r < 0) A L O G E ( " f a i l e d t o s e t l e d s ! " ) ;
close(fd);
} else { err = -errno;}
pthread_mutex_unlock(&g_lock);
return err;

LED的操作通过对字符设备的ioctl控制完成,write—leds()函数被set_light_ leds()调用;
LCD背光部分的操作通过在set_light_backlight()函数中写sys文件系统完成,与其他的系统
类似。它们都被设置为光设备的set_light函数指针。

�310
第19章
振动器系统

19.1 振动器系统概述

振动器负责控制电话来电时的振动功能,Android中的振动器系统是 个专提供这方面
功能的小系统,提供根据时间振动的功能。
振动器系统包含了驱动程序、硬件抽 象层、JNI部分、Java框架类等几个 部分,也向
Java应用程序层提供了简单的API作为平台接口。
Android振动器的相关内容如表19-1所示。

表 19-1 振动器的相关内容
Android 的层次 振动器系统部分 描 述

硬件层次 控制振动效果的 G PIO 输 出引脚

操作系统层 timed_output 驱动或者其他 输出型驱动程序,结构比较简单

本地层 硬件封装库和系统服务器中的JN] 被系统服务器的 Java 部分调用

Java 框 架层 VibratorService 在 com .android.server 当中,属于系统服务器

Java 层 的 A P I Vibrator 在 andro1d.os 包 中

19.2 振动器子系统的结构

19.2.1 振动器部分的结构
Android振动器系统自下而上包含了 驱动程序、振动器系统硬件抽象层、 振动器系统
Java框架类、Java框架中振动器系统使用等几个部分,其结构如图19-1所示。
自下而上,Android的振动器系统分成以下部分。
(l)驱动程序
通常基千 A ndroid 的 T im ed Output驱动框架实现。
• 0 0 Android板级支持与硬件相关子系统

立巴已邑业且 occccccccc - 二
,,,,,_ 一

一一

一~
___
_
__
_
__
IVibratorService.aidl

VibratorService
Java 框架

:I
本地框架



言;; -·
r-= --
---
-----
-
,:-
·
---
----
- -
工:
-= -一口
诅堕一部一生一一一___J
Vibrator

图 19- 1 Android 振动器系统的结构

(2) 硬件抽象层
e hardware/libhardware—legacy/include/hardware—legacy/vibrator.h: 硬件抽象层头文件。
ft hardware/libhardware _legacy/vibrator/vibrator.c: 硬件抽象层的默认实现。
振动器的硬件抽象层通常并不需要重新实现,是libhardware_legacy.so的 一 部分。
(3) JNI部分
e frameworks/base/serv1ces/jni/com android server VibratorService. cpp。
这个类是振动器的JNI部分,通过调用硬件抽象层向上层提供接口。
(4) Java部分
e frameworks/base/services/java/com/android/serverNibratorService.java 。
ft frarneworks/base/core/java/android/os/IVibratorService.aidl。
VibratorService.java文件实现了android.server包中的VibratorService, VibratorService
实现了IVibratorService.aidl定义的基于Binder的接口。
ft frarneworks/base/core/java/android/os冈brator.java 。
android.as包中的Vibrator类是振动器部分Java层的API类,通过调用AIDL实现。
(5) Java的API和应用层调用者
android.as包中的Vibrator类是振动器部分的平台APL可以在各个应用程序中调用。

�19.2.2 JNI部分
frameworks/base/services/jni/目录中的com_android_server_VibratorService.cpp文件是
Vibrator硬件抽象层的调用者,它同时也向Java提供JNI支待。
其中,为JNI定义的方法列表如下所示:
static JNINativeMethod method table[] = {
{ "vibratorOn", "(J) V", (void*) vibratorOn }, //振动器开
{ "vibratorOff", " ()V", (void*) vibratorOff } // 振 动 器 关
);
int regis 七er_android_server_Vibra七orService (JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
method table, NELEM(method table));

�312
第19章振动器系统 o o e

vibratorOn()和vibratorO钗)两个函数的实现分别如下所示:
S七atic v o i d v i b r a t o r O n ( J N I E n v *env, jobject clazz, j l o n g timeout_ms) {
vibrator_on(timeout_ms);

sta七ic v o i d vibra七orOff(JNIEnv *env, jobject clazz) {
vibrator_off ();

�19.2.3 Java框架部分
VibratorService.java通过调用JNI来实现com.android.server包中的VibratorService类。
frameworks/base/core/java/android/os/目录中的Vibrator.java文件实现了android.os包中
的Vibrator类。它通过调用vibrator的Java服务来实现(获得名称为vibrator的服务),配
和同目录中的IVibratorService.aidl文件向应用程序层提供Vibrator的API。

19.3 振动器BSP部分的结构

迪 19.3.1 驱动程序
Vibrator的驱动程序只需要实现振动的接口即可,这是 一 个输出设备,需要接收振动时
间作为参数。由于功能比较简单,因此Vibrator的驱动程序可以使用多种方式来实现。
在Android中,推荐基于Android内核定义Timed Output驱动程序框架来实现Vibrator
的驱动程序。
Timed Output的含义为定时输出,用于定时发出某个输出。实际上,这种驱动程序依
然是基于sys文件系统来完成的。
drivers/staging/android/目录timed_output.h中定义timed_output_dev结构体,其中包含
enable和get_time这两个函数指针,实现结构体后,使用timed_output_dev—registerO和
timed_output_dev_ unregister()函数注册和注销即可。
Timed Output驱动程序框架将为每个设备在/sys/class/timed_outputJ目录中建立 一 个子
目录,设备子目录中的enable文件就是设备的控制文件。读enable文件表示获得剩余时间,
写这个文件表示根据时间振动。
T皿ed Output驱动的设备调试,通过sys文件系统实现即可。
对于Vibrator设备,其实现的Timed Output驱动程序的名称应该为"vibrator"。因此
Vibraior设备在sys文件系统中的方法如下所示:
# echo "10000" > / s y s / c l a s s / t i m e d _ o u t p u t / v i b r 扛 o r / e n a b l e
# cat /sys/class/timed_output/vibrator/enable
3290
# echo " 0 " > /sys/class/timed_output/vibrator/enable

” ”
对于enable文件,“写 表示使能指定的时间,“读 表示获取剩余时间。

313�
• O O Android板级支持与硬件相关子系统

� �9.3.2 硬件抽象层的内容

1. 硬件抽象层的接口
Vibrator硬件抽象层的接口在hardware/libhardware_ legacy/include/hardware_ legacy/目录
的vibrator.h文件中定义:
i n t vibrator—on(int timeout_ms); //开始振动
J.吐vibrator_off (); //关闭振动

vibrator.h文件中定义两个接口,分别表示振动和关闭,振动开始以毫秒(ms)作为时
间单位。

己 提 示 : Timed Output类型驱动本身有荻得剩余时间的能力(读enable文件),但是
在Vibrator硬件抽象层以上的各层接口都没有使用这个功能。

2. 标准硬件抽象层的实现
Vibrator硬件抽象层具有标准的实现,在hardware/libhardware_legacy/vibrator/目录的
vibrator.c中。
其中实现的核心内容为sendi叨函数,这个函数的内容如下所示:
# d e f i n e THE_DEVICE " ; s y s / c l a s s / t i 而 e d _ o u 七 p u t / v i b r a t o r / e n a b l e "
s t a t i c i n t s e n d i t ( i n t timeout_ms)

i n t nwr, r e t , f d ;
char value[20];
# i f d e f QEMU_HARDWARE //使用QEMU的情况
if (qemu_check ( ) ) (
return qemu_control_command("vibrator:%d", timeout_ms);

#endif
f d = o p e n ( T H E _ D E V I C E , O_RDWR); //读取sys文件系统中的内容
i f ( f d < 0) r e t u r n e r r n o ;
nwr = s p r i n t f ( v a l u e , "%d\n", timeout_ms); //产生设置的数据
ret = write(fd, value, nwr);
close(fd);
r e t u r n ( r e t == n w r ) ? 0 : - 1 ;

“ ”
sendit()函数负责根据时间 振动 :在真实的硬件 中,通过 sy s 文件系统 的文件进行控
制;如果是模拟器环境,则通过QEMU发送命令。
vibrator_o叫)调用sendit()以时间作为参数,vibrator_on()调用sendit()以0作为参数。

19.4 振动器BSP部分的实现

如果振动器部分的驱动程序基千Android提供的Timed Output框架,不需要再实现特
定的硬件抽象层。

�314
第19章振动器系统 ooe

因此,针对特定的硬件平台,振动器系统的移植有两种方法。

o第 :由千 已经具有硬件抽象层 ,振动器 系统 的移植只需要实现
种方法 (通常情况 )
驱动程序即可。这个驱动程序需要基于 A ndroid 内核 中的 T im ed Output 驱动框架 。
o第二种方法:根据自己实现的驱动程序,重新实现振动器的硬件抽象层定义接口(需
要在 libhardware_ legacy.so 库 中 )
,由于振动器硬件抽象层 的接 口非常简单 ,因此这
种实现方式也不会很复杂。

�19.4.1 Nexus One系统的实现


Nexus One 系统使用 M SM 的 m 血 m ahi平 台中的振动器典型实现 。
Vibrator 的驱动程序在平 台支持 的 arcb/arm /m ach-m sm /目录 中的 m sm _ vibrator.c 文件 中
实现。 m sm _vibrator.c 中的核心实现是 set_p m ic_ vibrator() 函数 ,其实现 内容如下所示 :
static void set_pmic_vibrator(int on)

S七atic s t r u c t m s m rpc e n d p o i n t v i b endpoint; //定义RPC的端点
struct set_vib_on_off_req (
s t r u c 七 r p c _ r e q u e s t _ h d r hdr;
u i n t 3 2 _ t data;
I req;
if (!vib_endpoint) {
v i b e n d p o i n t = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0);
II省略部分内容

if (on) r e q . d a t a = c p u _ t o _ b e 3 2 ( P M I C _ V I B R A T O R _ L E V E L ) ; //得到请求时间
e l s e r e q . d a t a = c p u _ t o _ b e 3 2 (0);
msm_rpc_call(vib_endpoint, HTC PROCEDURE_SET_VIB_ON_OFF, &req,
sizeof(req), S * H Z ) ; //进行RPC调用

set_pmic _ vibrator() 函数通过 M SM 系统 的远程过程调用 (R PC ) 实现 了具体 的功能 ,


调用的指令由 H T C _ P R O C E D U R E _ SET_ VIB _ O N _ O F F 指定 。
这个驱动程序的初始化过程如下所示:
v o i d _ i n i t rnsm_init_pmic_vibrator(void) {
INIT_WORK(&vibrator_work, update_vibrator); //建立消息队列
s p i n _ l o c k _ i n i t (&vibe_lock);
v i b e _ s t a t e = O;
hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); / / 定 时 器
vibe_timer.function = vibrator_timer_func;
七imed_output_dev_register (&pmic_ vibrator); //注册timed_output_dev设备

vibrator_ work 为 w ork_struct 类 型 ,在 队 列 的执 行 函数 update_vibrator 中 ,调 用


set_pmic _vibrator() 函数 。
pmic _ vibrator 是一 个 tim ed_output_ dev 类型 的设备 。其 中的 enable 函数指针 的实现就
是 vibrator_enable, 它根据输入的数值开始定时器,并通过向调度队列进行输出操作。
get —tim e 函数指针 的实现 vibrator_get_tirne 则只是从定时器 中获取剩余 时间。
这里之所以使用定时器加队列的方式,是因为 enab le 的调用将 形成 一 个 持续 时 间的

315
•oo Android板级支持与硬件相关子系统

效果,但是调用本身不宜阻塞,因此实现就让vibrator_enable函数退出,通过定时器实现
效果。

�19.4.2 Nexus S系统的实现


Nexus S系统的振动器部分的实现,是 一 个利用 G PIO 控制 PW M 的设备 ,驱动程序 的
结构使用Android系统在内核中的Timed Output驱动架构。
使用典型的sys文件系统的控制方式,如下所示:
It e c h o "500" > / s y s / c l a s s / t

振动器的实现在平台的arch/arm/mach-s5pv2 l0/目录的herring-vibrator.c文件中实现,
主要的实现是herring_vibrator_enable()函数,也就是Timed Output的timed_output—dev结构
体enable函数指针,内容如下所示:
static void herring_vibrator_enable(struct t i m e d _ o u t p u t _ d e v *dev, int value) I
mutex_lock(&vibdata.lock);
hrtimer_cancel(&vibdata.timer); II取消定时器并设置GPIO
cancel_work_sync(&vibdata.work);
if (value) {
wake_lock(&vibdata.wklock); //加wake_lock锁
p w m c o n f i g ( v i b d a t a . p w m dev, P 础 D U T Y , P W M PERIOD) ; //配置PWM
pwm_enable(vibdata.pwm_dev);
g p i o _ d i r e c 豆 o n _ o u t p u t (GPIO_VIBTONE_ENl, G P I O _ L E V E L _ H I G H ) ; // G P I O 设 置
if (value > 0) {
if ( v a l u e > M A X _ T I M E O U T ) v a l u e = M A X _ T I M E O U T ;
h r t i m e r start(&vibda七a. timer, //启动定时器
n s _ t o _ k t i m e ((u64) v a l u e * N S E C _ P E R _ M S E C ) , H R T I M E R _ M O D E _ R E L ) ;

} e l s e h e r r i n g _ v i b r a t o r _ o f f (); //结束振动
mutex_unlock(&vibdata.lock);

这个enable函数的实现,包含了输入给振动器的值,输入的值通过GPIO设置到硬件
当中,并且使用了HTC平台的定时器系统。使用wake_lock的目的是表示此阶段中也不能
是休眠状态。

�19.4.3 Galaxy Nexus


Galaxy Nexus的振动器硬件就是tuna板的内容,使用GPIO进行控制。其驱动程序采
用Android普遍的Timed Out的实现方式。
振动器驱动程序的代码路径为: arch/arm/mach-omap2/board-tuna-vibrator.c。
其中的内容就是基于标准的Timed Output驱动架构生成了振动器的设备,在用户空间
同样从sys文件系统进行控制。
Timed Out驱动enable函数指针由vibrator_enable()函数实现,如下所示:
static void vibrator_enable(struct timed_output_dev *dev, int value) (
mutex_lock(&vibda七a. lock);
hrtimer_cancel(&vibdata.timer); //定时器的操作
ii: (value) {
wake_lock(&vibdata.wklock);

�316
第19章振动器系统 ooe

vibrator timer init(); / / v i b r a t o r 的初始化


gpio_set_value(vibdata.gpio_en, l); / / 控 制 GPI O 的引脚
omap_dm_timer_start(vibdata.gptimer);
vibdata.enabled = true;
if ( v a l u e > 0) (
i f ( v a l u e > M A X _ T I M E O U T ) v a l u e = MAX TIMEOUT;
hrtimer start(&vibdata.timer, //启动定时器
n s t o k t i m e ( ( u 6 4 ) v a l u e * NSEC PER MSEC), HRTIMER MODE R E L ) ;

} else { vibrator_off(); // 控制振动器停止


mu七ex_unlock ( & v i b d a t a . l o c k ) ;

此处的vibrator的内容由缸timer结构和附加的信息组成,振动器通过设置GPIO引脚
控制硬件,hrtimer的作用是控制振动在 一 定的时间之后停止 。同样 的道理 ,vibrator_get_time
的数值通过定时器得到。

317�
第20章
电池信患部分

20.1 电池信息部分

Android系统考虑了多种供电方式,包括AC、USB、Battery等。在应用程序层次,通
常包括了电池状态显示的功能。因此,从Android系统的软件方面(包括驱动程序和用户
空间内容),需要在 一 定程度上 获得 电池 的状 态 。在 A ndroid 系统 中,电池 系统主要 负责 电
池信息统计方面的功能。
Android振动器的相关内容如表20-1所示。

表 20-1 振动器的相关内容
Android的层次 振动器系统部分 描 述

硬件层次 电池、直流电源的监浏硬件

操作系统层 Power Supply驱动 sys文件系统接口

本地层 系统服务器中的JNl 被系统服务器的Java部分调用

Java框架层 BatteryServtce 在com.android.server当中,属于系统服务器

Java层的API Battery Manager中的定义值 使用广播接收器的通知机制

20.2 电池信息子系统的结构

�20.2.1 电池系统部分的结构
Android的电池系统分为驱动程序、本地 — JNI 程序 、Java 框架程序等 几个部分 ,其结
构如图20-1所示。自下而上,Android的电池系统分成以下几个部分。
(I)驱动程序
特定硬件平台电池的驱动程序,可以使用LinU){的Power Supply驱动程序,实现之后
可以向用户空间提供信息。
第20章 电池信息部分 ooe

Java框架
--------一一一一令 一·
c cc c c一 一 -

Intent
BatteryManag_er

,,J av 框咎一二二二二工二二二二二二二二二二二二
BatteryService JNI
…·埠.恳..…………...一一.......
Linux内核层
巨;二__亡豆巨豆厂]
图20-1 Android电池系统的结构

(2) J N I 部 分 : 系 统 服 务 器 中 的 B a t t e r y S e r v i c e 的 本 地 实 现
e frameworks/base/services/jni/com_android_ server_BatteryService.cpp。
其中调用sys文件系统访问驱动程序,也同时提供了JNI的接口。
(3) J a v a 框 架 部 分 : 电 池 服 务 及 其 中 的 广 播
电池相关的内容包括BatteryService和android.os中的BatteryManager, 内部包
com.android.intemal.os当中的与Battery相关的部分。
e frameworks/base/services/java/com/android/server/BatteryService.java: 服务部分。
e frameworks/base/core/java/android/os/: 提供定义的BatteryManager。
e frameworks/base/core/java/corn/android/intemal/os/: 与Battery相关的内部部分。
BatteryService.j a v a 通 过 调 用 J N I 来 实 现 B a t t e r y Service类,BatteryM a n a g e r 类 中 定 义 了
一 些 J a v a 应 用 程 序 层 可 以 使 用 的 常 量 , 还 有 一 些 用 于 定 义 远 程 接 口的 aid l 文 件 。

�20.2.2 JNI部分
frameworks/base/services/jni/目录中的com_android_server_BatteryService.cpp文件是
Battery部分的本地和JNI部分。文件提供的方法列表如下所示:
static JNINativeMethod sMethods[J = {
{"na 七ive 'update",

"()V", (void*)android server BatteryService upda 七e },

这是com.android.server包中的BatteryService类的本地方法,只有实现"native_update"

方法的 个 函 数 ,这 个 函 数 没 有 参 数 , 也 没 有 返 回 值 。事 实 上 ,其 更 新 的 内容 是 通 过 直 接
写Java类中的属性完成的,因此没有在函数中完成。
定义文件系统的路径如下所示:
Jtdefine POWER_SUPPLY_PATH "/sys/class/powe 仁 S.2PP坟 ”

android—server_ BatteryService_ updateO函数实现的主要片段如下所示:


static void android_server_BatteryService_update(JNIEnv* env, jobject obj) {
setBooleanField(env, obj, gPaths.acOnlinePath, gFieldids.mAcOnline);
setBooleanField(env, obj, gPaths.usbOnlinePa 七h , gFieldids.mUsbOnline);
I I ..... 设赏batteryPresentPa 七h , mBatteryLevel, batteryVoltagePath等属性
const int S I Z E = 128;

319
• O O Android板级支持与硬件相关子系统

char buf[SIZEJ;
i f ( r e a d F r o m F i l e ( g P a t h s . b a t t e r y S t a t u s P a 七 h , b u f , S I Z E ) > 0) / / 设 置 状 态
env->SetintField(obj, gFieldids.mBatteryStatus, getBatteryStatus(buf));
else
env->SetintField(obj, gFieldids.mBatteryStatus,
gConstants.statusUnknown);

if ( r e a d F r o m 豆 l e ( g P a t h s . b a t t e r y H e a l t h P a t h , b u f , S I Z E ) > 0) //设控健康程度
env->SetintField(obj, gFieldids.mBatteryHeal七h, getBatteryHeal七h(buf));

if ( r e a d F r o m F i l e ( g P a t h s . b a t t e r y T e c h n o l o g y P a t h , b u f , S I Z E ) > 0)
env->SetObjectField(obj, gFieldids.mBatteryTechnology,
env->�ewStringUTF(buf));

从驱动中获取的各种属性的处理,在注册函数register_android_ server_ BatteryService()


进行,主要内容如下所示:
i n t r e g i s t e r _ a n d r o i d _ s e r v e r _ B a t t e r y S e r v i c e ( J N I E n v * env) {
char path[PATH_MAX];
struct dirent* entry;
D I R * d i r = opendir(POWER_SUPPLY_PATH); //打开sys文件系统
II省略错误处理
while ((entry = readdir (dir))) {
c a n s t c h a r * name = e n t r y - > d _ n a m e ; II找到子目录
i f ( n a m e [ O ] = = ' . ' && ( n a m e [ l ] = = O I I ( n a m e [ l ] = = ' . ' & & name[2]==0))) {
continue;

char buf[20];
II得到的表示类型的路径: / s y s / c l a s s / p o w e r _ s u p p l y / { 设 备 名 称 } / t y p e
s n p r i n t f ( p a t h , s i z e o f ( p a 七 h ) ' " 令 s / % s / t y p e " , POWER_SUPPLY_PATH, name);
i n t l e n g t h = readFromFile(path, buf, s i z e o f ( b u f } } ;
i f ( l e n g 七 h > 0) {
if ( b u f [ l e n g t h - 1] = = ' \ n ' )
b u f [ l e n g t h - 1) = 0 ;
if ( s t r c m p ( b u f , " M a i n s " ) == 0) { / / 类 型 为 " M a i n s " (主供电设备)的处理
snprin七f(path,sizeof(path),"%s/吊s/online",POWER_SUPPLY_PATH,name);
if ( a c c e s s ( p a t h , R_OK)==O) g P a t h s . a c O n l i n e P a 七 h = s t r d u p ( p a t h ) ;
} else if(strcmp(buf,"USB")==O) { / / 类 型 为 " U S B " (USB供电)的处理
s n p r i l ' ) t f ( p a t h , s i z e o f ( p a t h ) , 飞s/钰/online'勹POWER_SUPPLY_PATH,name);
i f ( a c c e s s ( p a t h , R_OK) = = O ) g P a t h s . u s b O n l i n e P a t h = s t r d u p ( p a t h ) ;
} e l s e i f ( s t r c m p ( b u f , " B a t t e r y " ) = = O) { / / 类 型 为 " B a t 七 e r y " ( 电 池 ) 的 处 理
snprintf(path,sizeof(pa七h)'"兮s/毛s/online",POWER_SUPPLY_PATH,name);
if ( a c c e s s ( p a t h , R OK) = = 0) g P a t h s . b a t t e r y S t a 七 u s P a t h = s t r d u p ( p a t h ) ;
II 省略部分内容:电池设备的特定处理

closedir(dir);
// 省 略 部 分 内 容
jclass clazz = env->FindClass("com/android/server/BatteryService");
//省略:获得com.android.server.BatteryService中成员
clazz = env->FindClass("android/os/Bat七eryManager");
// 省 略 : 获 得 a n d r o i d . o s . B a t t e r y M a n a g e r 中 常 般
return jniRegisterNativeMethods(env, "com/android/server/BatteryService",
sMethods, NELEM(sMethods));

�320
第20章 电池信息部分 ooe

处理的流程是根据设备类型判定设备后,得到各个设备的相关属性,需要得到更多的
信息。例如,如果是交流或者USB设备,只需要得到它们是否在线ConLine); 如果是电
池设备,则需要得到更多的信息,例如状态(status)、健康程度(health)、容量(capacity)、
电压(voltage_now)等。

�20.2.3 Java部分
BatteryService.java文件实现了com.android.server包中的BatteryService类,这个类本
身继承了Binder。它运行千系统服务器的进程system_server中。BatteryService通过
sen dB road cast发送广播的Intent, 由Android系统的各个方面通过实现BroadcastReceiver
接收获得电池信息的变化。
android.content包中的Intent中有几个action与电池部分相关,内容如下所示。
o电池变化: "android.intent.action.BATTERY CHAI'寸GED"。
o电蜇低: "android.intent.action.BATTERY_ LOW"。
o电池状态完好: "android.intent.action.BATTERY_OKAY"。
o电源连接变化: "android.intent.action.ACTION _ P O W E R _CONNECTED"。
o电源断开连接: "android.intent.action.ACTION _ P O W E R _DISCONNECTED"。
BatteryManager.java文件的BatteryManager类定义了Battery系统对应用程序层的常量,
各个常量用于接收ACTION_B A T T E R Y _CHANGED的Intent中的附加信息,如下所示:
p u b l i c s t a t i c final S七ring E X T R A _ H E A L T H // "health"
p u b l i c s t a t i c final S t r i n g E X T R A _ P L U G G E D // " p l u g g e d

BatteryStats .java类为内部使用,实现了 一 个Parcelable 类。内部包com .android.intem al.os


中的BatteryStatslmpl类实现了BatteryStatslmpl.aidl和IBatteryStats.aidl定义的内容。
系统服务器当中的PowerManagerService、Watchdog等是BatteryService的调用者。

20.3 电池信息BSP部分的结构

电池系统在驱动程序层以上的部分都是Android系统中默认的内容。在移植过程中基
本不需要改动。电池系统需要移植的部分仅有驱动程序部分。
Battery驱动程序使用Linux标准的Power Supply驱动程序,与上层的接口是sys文件
系统,主要用于读取sys文件系统中的文件来获取相关的电池信息。
Battery驱动程序需要通过sys文件系统向用户空间提供接口,sys文件系统的路径是由
上层的程序指定的。
Linux标准的Power Supply驱动程序所使用的文件系统路径为/sys/class/power—supply,
其中的每个子目录表示 一 种能源供应设备的名称。
Power Supply驱动程序的头文件在include/血ux/power_ supply.h中定义,注册和注销驱
动程序的函数如下所示:
int power_supply_register(s七ruct d e v i c e *paren七,struct p o w e r _ s u p p l y *psy};
v o i d power_supply、_unregister(struct powe竺_supply *psy) ;

321�
• O O Android板级支持与硬件相关子系统

其中power_supply结构体为驱动程序需要实现的部分,其内容如下所示:
struct p o w e r _ s u p p l y {
canst char *name; //设备名称
e n u m power_s u p p l y _ t y p e 七 y p e ; //类型
e n u m p o w e r _ s u p p l y _ p r o p e r t y *properties; II属性指针
size_七num_properties; //属性的数目
char **supplied_to;
size_t num_supplicants;
int (*get property) (struct p o w e r supply *psy, //获得属性
e n u m p o w e r _ s u p p l y _ p r o p e r t y psp, union p o w e r _ s u p p l y _ p r o p v a l *val);
v o i d (*ex七ernal power_changed) (struct p o w e r _ s u p p l y *psy);
』.
.' II 省略部分内容

get_property和external_power_ changed这两个函数指针具体驱动程序中主要实现的内
容。每实现 一 个 pow er_supply设备,将在/sys/class/power_supply中出现 一 个子 目录 ,子 目
录中的各个内容为它的属性。
Power Supply驱动程序是 一 个提供信息输入型驱动 ,其调试通常也只需要通过 sys 文
件系统即可。

20.4 电池信息BSP部分的实现

20.4.1 模拟器中的实现
模拟器环境的goldfish内核实现了电池部分的驱动程序,代码为drivers/power/目录中
的goldfish_battery.c文件。
在这个驱动程序中,共注册了两个Power Supply设备,分别是"battery" (电池)和
"ac" (直流电)。在goldfish_battery_probe()函数中构造了power_supply类型的结构,调用
power_ supply_register()函数将其注册到系统中。
goldfish_battery_ interrupt()是这个驱动中使用的中断处理函数,这里使用的中断号实际
上 是 1 7 (名称为goldfish-battery)。函数的实现如下所示:
S七atic irqreturn_t g o l d f i s h _ b a t t e r y _ i n t e r r u p t ( i n t irq, v o i d *dev_id) {
u n s i g n e d long irq_flags;
struct g o l d f i s h _ b a t t e r y _ d a t a *data = d e v _
uint32一七 S七atus;
spin lock irqsave(&data 今 lo c k , i r q _ flags) ; / /保存中断状态
//读取状态位,也会消除中断
status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS);
S七atus &= B A T T E R Y INT MASK;
if (status & BATTERY_STATUS_CHANGED)
p o w e r s u p p l y changed(&data->battery); //更新"battery"设备的信息
if (status & AC_STATUS_CHANGED)
power_supply_changed(&data->ac); //更新"ac"设备的信息
spin_unlock_irqrestore(&data->lock, irq_flags); //恢复中断状态
return s t a t u s ? I R Q _ H A N D L E D : IRQ_NONE;
l

这里处理的实际内容来自模拟器的虚拟寄存器和虚拟中断。当Power Supply发生变化

�322
第20章 电池信息部分 ooe

时,模拟器环境将自动更新虚拟寄存器信息,并触发电池相关的中断,由本驱动程序读取
虚拟寄存器,然后通过Power Supply驱动框架更新sys文件系统中的内容。
在用户空间的命令行中,查看sys文件系统内容如下所示:
# cat /sys/class/power_supply/ac/type
Mains
# cat /sys/class/power_supply/ac/online
1

名 称 为 " a c " 的 P o w e r Supply设备类型为主供电设备(Mains, 由POWER_SUPPLY_


TYPE_M躯S宏定义),online的数值为l, 表示为当前在线。

查看另外 个设备的 内容如下所示 :
鲁cd /sys/class/power_supply/battery/
lf c a 七 t y p e s t a t u s h e a l t h p r e s e n t t e c h n o l o g y c a p a c i t y
Battery
Charging
Good
1
Li-ion
50

此处sys文件系统,目录中设备的名称为"battery", 表示设备类型为电池(Battery,
由POWER_ SUPPLY_TYPE_BATTERY宏定义),显示的状态为:正在充电,健康状况好,
目前在使用,适用Li-ion技术,容量为50。

迪 20.4.2 Nexus One


DS2784是 一 种独立式单节 电量计 芯片 ,可 以通过测量 电压 、温度和 电流 ,结合 电池 的
特性估算电池电量,器件带有EEPROM并且提供1-W 订 e 总线 串行通信 的连接方式 。
在内核的驱动中,drivers/power/ds27 84_ battery. c为电池部分的驱动,注册成名称为
"ds2784-battery"的平台驱动,其探测函数如下所示:
s t a t i c i n t ds2784_battery_probe(struct platform_device *pdev){
int re;
s t r u c t ds2784_device_info * d i ;
s t r u c t ds2784_platform_data *pdata;
di= k z a l l o c ( s i z e o f ( * d i ) , GFP_KERNEL);
if ( ! d i ) r e t u r n -ENOMEM;
platform_set_drvdata(pdev, di);
pdata = pdev->dev.platform_data;
II 省略错误处理内容
di->charge = pdata->charge;
di->wl_slave = pdata->wl_slave;
d i - > d e v = &pdev->dev; I I 为 设 备 ( s 扛 u c t d e v i c e ) 赋值
di->bat.name = " b a t t e r y " ; I I 表 示 st r uct power_supply结构的各个成员
d i 今 b a t . t y p e = PO郎R SUPPLY TYPE BATTERY;
di->bat.prope工ties = battery_properties; II P o w e r S u p p l y 的属性
d让>bat.num_properties = ARRAY_SIZE(battery_properties);
di->bat.external_power_changed = battery_ext_power_changed;
di->bat.get_property = battery_get_property;
di->last_charge_mode = Oxff;
工c = p o w e r _ s u p p l y _ r e g i s t e r ( & p d e v - > d e v , & d i - > b a t ) ; II注册设备
INIT_wORK(&di->monitor_work, ds2784_battery_work); II建立工作线程

323�
• 0 0 Android板级支持与硬件相关子系统

d i - > m o n i t o r w q u e u e = create_freezeable_workqueue(dev_name(&pdev->dev));
// 省略部分内容

battery_properties的类型为power_supply_property, 表示此处的Power Supply驱动所需


要使用到的属性列表。
ds2784_battery—work()函数是 一 个线程的实现体,在运行中进行了电池电量的估算工
作。其中,调用的硬件操作通过wl_ds2784—io()函数完成,进而调用wl_write_8()写设备的
总线,此部分内容就是通过WI总线Cl-Wire)写ds2784设备。

�20.4.3 Nexus S
Nexus S系统中的电池信息通过调用管理芯片MAX8998来获取,Power Supply驱动是
如vers/power/目录中的s5pc 110_battery.h和s5pc 110_battery.c。
Nexus S的Power Supply驱动具有battery、ac和usb几种内容。例如,在USB充电状
态下,几个Power Supply驱动信息的内容如下所示:
# cat / s y s / c l a s s / p o w e r _ s u p p l y / b a t t e r y / s t a t u s
Charging
# cat / s y s / c l a s s / p o w e r _ s u p p l y / b a t t e r y / c a p a c i t y
85


# cat / s y s / c l a s s / p o w e r _ s u p p l y / a c / o n l i n e

# cat /sys/class/powe 工_ sup p ly /u sb /o n l in e


1

驱动实现s5pc 110_battery.c文件的当中,几个供电设备被称之为charger (充电器),其


探测probe()函数如下所示:
static _ d e v i n i t int m a x 8 9 9 8 _ c h a r g e r _ p r o b e ( s t r u c t p l a t f o r m _ d e v i c e *pdev) {
struct m a x 8 9 9 8 _ d e v 五 o d e v = dev_get_drvdata(pdev->dev.parent);
struct m a x 8 9 9 8 _ p l a t f o r m _ d a t a *pdata = dev_get_platdata(iodev->dev);
struct c h g _ d a t a *chg;
int ret = 0;
c h g = kzalloc(sizeof(*chg), GFP_KERNEL); //填充表示charger设备的结构体
// 省略错误处理内容
c h g - > i o d e v = iodev;
chg->pdata = pdata->charger;
// 省略错误处理内容
chg 今 p sy_b a t .n am e = "battery", //电池设备
c h g - > p s y _ b a t . t y p e = POWER_SUPPLY_TYPE_BATTERY, //电池设备的类型
c h g 今 p sy_b a t .p rop e r t ie s = max8998_battery_props, //电池设备的操作
chg 今 p sy_b a t .n um_ p rope rt ie s = ARRAY_SIZE(max8998_battery_props),
c h g - > p s y _ b a t . g e t _ p r o p e r t y = s3c_bat_get_property,
c h g 今 p sy_ u sb .n am e = "usb", // USB供电设备
c h g 今 p sy_ u sb .t yp e = POWER_SUPPLY_TYPE_USB, // USB供电设备的类型
c h g - > p s y _ u s b . s u p p l i e d _ t o = supply_lis七, // USB供电设备的操作
c h g - > p s y _ u s b . n u m _ s u p p l i c a n t s = ARRAY_SIZE(supply_lis七),
chg 今 p sy_ u sb .p rop e r t ie s = s3c_power_properties,
c h g 今 p sy_ u sb .n um _ p rop e r巨 e s = ARRAY_SIZE(s3c_power_properties),
c h g - > p s y _ u s b . g e t _ p r o p e r t y = s3c_usb_get_property,
// 省略:直流充电设备"ac",
chg->present = l; I I 设 置 c h a r g e r 设 备 的 一 些状态
尘 9 二 P 吐 J i n g __i!")):_ 笘yal = POLLING J;N.'.l'J;;F-_Y.A店

324
第20章 电池信息部分 ooe
chg->ba七一info.batt_health = POWER_SUPPLY_HEALTH_GOOD;
c h g - > b a t _ i n f o . b a t t _ i s _ f u l l = false;
c h g - > s e t _ c h a r g e _ t i m e o u 七 = false;
c h g - > c a b l e _ s t a t u s = CABLE_TYPE_NONE;
mutex_init(&chg->mutex);
platform_set_drvdata(pdev, chg}; / / 获 得 c h a rge r 的平 台设 备
ret = p o w e r _ s u p p l y register(&pdev->dev, &chg 今 p s y bat); / /处理电池设备
if (ret) ( // 省略错误处理内容 )
II 省略: U S B 充 电设备和直流 充电设备
ret = request_threaded_irq(iodev->i2c_client->irq, NULL,
max89 9 8 int w o r k func,
IRQF_TRIGGER_FALLING, "max8998-charger", chg);
if (ret) { // 省略错误处理内容 )
ret = e n a b l e irq wake(iodev->i2c_client->irq);
II 省略错误处理内容
wake_lock(&chg->work_wake_lock);
queue_work(chg->monitor_wqueue, &chg->bat_work);
return 0;

其中使用的结构max8998_dev, 表示的就是电源管理芯片M心伐998。由于电池的信息
和电源管理部分相关,因此需要使用MAX8998驱动的内容。

�20.4.4 Galaxy Nexus


Galaxy Nexus硬件上采用M心( IM 的 M 心<17040 作为检测电池充电信息,这是一 个连
接于l2C总线的芯片,USB和交流供电的信息则通过另外的方式得到。
驱动的代码路径为: drivers/power/max 17040 _battery.c和pda_power.c, 前者用于
M心(17040检测电池(battery)的变化,后者为检测外接电源(usb和ac)变化。
maxl 7040_battery.c实现了"max 17040"设备,其探测probe()函数完成了power_supply
设备的注册,其中还有 一 个名称为 m axi7040_ale氓)的中断处理函数。
pda_power.c实现了"pda_power"设备,其探测probe()函数完成了usb和ac的检测,
因此当中有两个power_supply设备。两个设备共用power_changed_isr() 一 个中断处理函数,
电源状态的信息就是由中断汇报的。
在sys文件系统的/sys/class/power_supply/中,有battery、usb和ac三个目录。而
MAX17040在l2C总线的地址为4-0036, 在/sys/bus/i2c/devices/4-0036/目录中有相关的信息。
板级文件board-tuna-power.c定义了关于max17040的相关信息,如下所示:
static canst _ i n i t d a t a struct i2c_board_info maxl7043_i2c[] = {
I2C B O A R D INFO("maxl7040", (Ox6C >> 1)), // Ox6C >> 1 等于 Ox 3 6
.platform_data = &max17043_pdata,
.irq = O M A P G P I O IRQ (GPIO F U E L ALERT) , // 使用了 G P IO 的中断

实际上,电池电量的更新是通过GPIO的中断来通知的,因此设置了中断号。
同样,board-tuna-power.c的初始化函数omap4_tuna_power_ init()也注册了"pda_power"
设备,并进行了相关GPIO的设置。

325�
第21章 _
Android 4.x 的音频、视频系统

21.1 Android 4.x的音频系统

21.1.1 音频系统的结构

Android 4.x的音频系统的结构产生了 些变化 ,音频 系统 的结构如 图 2 1-1 所示 。

Java应用层

Java框架层

Audio
本地API

audio. <XXX> .so

Linux内核层 赞
Audio Driver
i
-

图 21-1 Android 4.x 的音频系统结构

音频硬件抽象层的主要改变在硬件抽象层之下的部分;由于需要对硬件抽象层进行调
用,AuidoFlinger的实现也发生了变化; Java层次的结构变化不大,也保证了对应用层API

的兼容性。音频硬件抽象层的实现,原本是 个被 固定链接动态库 ,变成 了 A ndroid 标准
的硬件模块。与其他的硬件模块相比,Android 4.x的音频硬件模块具有多种类型,例如主
要实现、效果实现、蓝牙实现等,它们分别以单独的动态库存在。
第21章 Android 4.x 的音频 、视频系统 ooe

汕 2 1.1.2 音频框架层

1 . AudioFlinger
Android 4.x 的 A udiof linger 与 以前 的版本基本相 同,只是使用 了新的硬件抽象层 ,
Audi of linger 中的 load_audio_interface() 用千加载模块形式 的硬件抽象层 。
ISchedulingPolicyService. * 和 SchedulingPolicyService.*表示了 A udio 策略 的调度 实现 ,
是 A ndroid 4.x 版本 中新增 的 ,其 中定义 了根据进程进行 A udio 调度 的接 口,如下所示 :

ISchedulingPolicyService 是 IPC 的接 口,SchedulingPolicyService 则是对上层 的接 口,


此部分可以跨进程实现,其真正的实现者是 libscheduling_policy.so 库 。

2. tinyalsa
tinyalsa 是 一 个 调 用 了 A L SA 驱 动 程 序 的库 , 实 际 上 也 就 是 A L SA 用 户 空 间库
(libasound.so) 的 A ndroid 版 本 。血 yalsa 的代 码 路 径 为 extem al/tinyalsa/, 生成动态库
libtinyalsa.so, 以及用千调试的可执行程序 tinyplay 、tinycap 和 tinym ix 。
tinyalsa 在 A ndroid 4.x 的音频系统 中不是必需 的,只为使用 A L SA 驱动 的系统所用 。
在 include/血 yalsa/ 目录 中,asoundlib.h 是其头文件 ,该文件 中定义的函数与 A L SA 库相 同,
以"pcm "为开头 的是数据通道 函数 ,以"m ix_ctl"为开头的是混音器控制 函数 。因此 ,原本调
用 A L SA 库 的程序 ,可 以直接 替换成 tinyalsa。

�21.1.3 音频BSP部分结构

1. 总体结构
与很多其他硬件系统类似,音频 B SP 部分 的头文件在 libhardw are/include/hardw are/目
录中,但是包括了多个文件,如下所示。
• audio.h: Audio 基本 的控制和数据设备接 口。
• audio _policy.h: Audio 策略接 口。
• audio_effect.h: Audio 效果接 口。
根据以上头文件,硬件模块实现之后,将生成若干个硬件模块的动态库,放置于目标
文件系统的 /sytem /lib/hw /目录 中。
audio.h 的实现 主要是 audio_h w_device 结构 ,可 以包括若干个库 ,如下所示 。
• audio.primary.default.so: 默认的音频实现。
• audio.primary. < platform >.so: 某个特定平台的音频实现。
• audio.a2dp.default.so: 默认蓝牙的音频实现。
audio. usb. default. so: 默认 U SB 音频 的实现 。
Android 系统 中硬件模块 的动态库通常 的命名方式是<类型>.<平 台>,而音频 的硬件 模
块的动态库的命名则是 <类型>.<子类型>.<平 台>,因此 prim ary 、a2dp 、usb 等分别代表 的

327�
•oo Android 板级支持与硬件相关子系统

都是音频方面的子类型, a u d io .p rim ary 是库 的固定实现 ,对应于 以前版本 的 lib au d io .so 。


audio_policy.h 的实现 主要是 au d io _policy_device, 可以包括若干个库,如下所示。
a audio_policy.default.so: 默认蓝牙的音频策略实现。
a audio _policy. < p l a t f o r m > .so: 某个特定平台的音频策略实现。
此处生成的是音频部分的策略库,对应千以前版本的 lib au d io _policy.so 。

2. 硬件抽象层的接口
由千使用了 A n d ro id 标准硬件模块 的形式 ,A n d ro id 4.x 音频硬件抽象层 的接 口使用 了
纯 C 语言形式 的方式 的接 口。
au小o.h 头文件 中定义 的 au d io _ stream—
o u t 结构表示输 出流 ,如下所示 :

struct audio _ s t r e a m _ o u t {
struct a u d i o _ s t r e a m common;
u i n t 3 2 _ t (*get_latency) (canst s七ruct audio_stream_out *stream);
int (*se七_volume) (struct audio_stream_out *stream, float left, float right);
ssize_t (*write) (struc七audio_stream_out *stream, const void* buffer,
size_t bytes);
int (*get_render_position) (const struct audio_s七ream_out *stream,
uint32 t *dsp frames);
int (*get_next_write_timestamp) (const s七rue七audio_s七ream_out *stream,
int64_t *timestamp);
};
吕屯旦屿f ,E坒旦ct a主dio_
, tream_o1,1t audio_stream_out_妇


audio stream _ in 结构表示输入流 ,如下所示 :
struct audio s t r e a m in {
struct a u d i o _ s t r e a m common;
in七(*set_gain) (struct a u d i o _ s t r e a m _ i n *s七ream, float gain);
ssize_t (*read) (s七ruct audio_stream_in *stream, void* buffer, size_t bytes);
uint32_t (*get_inpu七一喊frames_lost) (struct a u d i o _ s t r e a m _ i n *stream);
);
typ色def struct audio. - s t r e a m- in
- audio- s t- r e a m_ l._Q t ;


a u d i o _ stream o u t 和 au d io _ s t r e a m _ in 分别用于音频数据 的输 出和输入 的数据流操作 ,

主要的函数指针为 w rite 和 rea d , 分别用于音频数据的写和读。
audio.h 中定义 的 au d io_ h w _ device 表示音频设备 ,结构如下所示 :
struc七audio_hw_device {
struct h w d e v i c e t common;
// 省略部分内容
int (*se七_parame七ers) (struct a u d i o _ h w _ d e v i c e *dev, const char *kv_pairs);
char * (*get主aramete'rs) (cons七struct a u d i o _ h w _ d e v i c e *dev,
const char *keys);
in七(*open_ou七put_stream) (struct audio_hw_device *dev,
audio io handle t handle, audio devices t devices,
audio_output_flags_七flags, struct a u d i o _ c o n f i g *config,
struct audio stream out **stream out);
v o i d (*close_output_stream) (struct audio_hw_device *dev,
struc七aus.io_stream_out* stream_ou七);
int (*open_input_stream), (struct audio_hw_device *dev,
a u d i o io h a n d l e 七 h a n d l e , audio devices t devices,
struct a u d i o _ c o n f i g *config, struct a u d i o _ s t r e a m _ i n **stream_in);
v o i d (*close_input_stream) (struct audio_hw_device *dev,

�328
第21章 Android 4 . x 的 音 频 、 视 频 系 统 ooe

struct audio stream in *stream in);


int (*dump) (const struct audio_hw_device *dev, int fd);
) ;
typedef struct audio_hw_device audio_hw_device_启

audio_h w _ d e v i c e 中 的 o p e n _output_s t r e a m 和 c l o s e _output_ s t r e a m 函 数 指 针 分 别 用 于 得


到输出和输入流的结构,除此之外,还有几个用于设置和获取的函数指针。

3. 策 略 的 接 口

audio _ p o l i c y . b 头 文 件 中 定 义 音 频 策 略 方 面 的 接 口 a u d i o _ p o l i c y 结 构 , 内 容 如 下 所 示 :
struct audio_policy {
int (*set_device_connection_s七ate) (s七ruct audio_policy *pol,
audio_devices_t device, audio_policy_dev_state_t state,
const char *device_address); //连接的状态
audio_policy_dev_state_t (*get_device_connection_state) (
const s七ruct audio_policy *pol, audio_devices_t device,
cons七char *device_address); //或者设备的连接状态
void (*set_phone_state) (struct audio_policy *pol, audio_mode_t state);
void (*set_ringer_mode) (struct audio_policy *pol, uint32_t mode, uint32_t mask);
void (*set_force_use) (struct audio_policy *pol, audio_policy_force_use_t usage,
audio_policy_forced_cfg_t config);
audio_io_handle_t (*get_output) (struct audio_policy *pol,
audio_stream_type_t stream, uint32_t samplingRate,
audio_format_t format, uint32_t channels,
audio_output_flags_t flags);
int (*start_output) (s七ruct audio_policy *pol, audio_io_handle_t output,
audio_stream_type_t stream, int session);
int (*stop_output) (struct audio_policy *pol, audio_io_handle_t output,
audio_stream_type_t stream, int session);
void (*release_output) (struct audio_policy *pol, audio_io_handle_t output);
audio_io_handle_t (*get_input) (struct audio_policy *pol,
audio_source_t inputSource, uint32一七 samplingRate,
audio_format_t format, uint32_t channels,
audio_in_acoustics_t acoustics);
int (*start_inpu七) (struct audio_policy *pol, audio_io_handle_t input);
int (*stop_input} (struct audio_policy *pol, audio_io_handle_t input);
void (*release_input) (struct audio_policy *pol, audio_io_handle_t input);
);

此 处 的 策 略 a u d i o_ p o l i c y 结 构 实 际 上 就 是 A u d i o 策 略 的 核 心 内 容 , 包 括 了 设 置 强 制 使
用的流、设置电话状态、对输入输出端口的控制等。
音 频 策 略 设 备 a u d i o_policy_d e v i c e 的 定 义 如 下 所 示 :
struct audio_policy_device {
struct hw_device_t common;
int (*create_audio_policy) (const struct audio_po扛cy_device *device,
struct audio_policy_service_ops *aps_ops,
void *service, struct audio_policy **ap);
int (*destroy_audio_policy) (const struct audio_policy_device *device,
struct audio_policy *ap);
);

上 层 使 用 A u d i o 策 略 的 执 行 流 程 是 先 得 到 a u d i o_policy_d e v i c e 结 构 , 用 其 中 的 函 数 指
针 A n d r o i d 策 略 的 接 口 a u d i o_policy, 然 后 调 用 各 个 A u d i o 策 略 的 函 数 。

audio _policy_ service_ o p s 结 构 则 代 表 了 个 由调 用 者 实 现 的 函 数 指 针 的 结 构 ,包 括 打

329�
• O o Androi'd 板级支持与硬件相关子系统

开和关闭输入输出环节的接口,以及输出环节的挂起和恢复。

21.2 Android 4.x 音频的 B SP 实现

�21.2.1 主实现和策略实现

音频系统的主实现包括了音频主要的控制 数据 接 口和 策 略库 实现 ,代 码 的路 径 为
hardware/libhardware/modules/audio/ , 其 中 的 内 容 将 生 成 aud io .prim ary.defau lt.so 和
audio.policy.stub.so 两个动态库 。
audio_hw.c 文件是音频主要硬件抽象层 的实现 ,读和写 的实现都是空实现 ,不会产 生
任何效果。
audio _policy.c 文件是音频策略硬件抽象层 的实现 ,策略 的实现都是空的,没有实际的
效果,作为回调的 aud io_policy_service_ops 结构也没有进行处理 。

逵21.2.2 仿真器实现
仿真器的音频硬件抽象层是针对仿真器的实现,在仿真器的环境中可以利用主机的资
源进行音频。这部分内容被视为 legacy 的实现 ,由以下两个部分组成 。
e hardware/lib hardware_legacy/audio: 表示 go ld fish 平 台的硬件抽象层 ,其 中的 内容生
成了动态库 libaud iohw _legacy.so 和 lib aud iop olicy_legacy.so 。
e device/generic/goldfish/ audio/: 将生成动态库 aud io .prim ary.go ld fish .so 。
此处的实现理念是将原本 A n droid 2.3 版本接 口的硬件抽象层 的实现封装成 A n dro id 4.x
模块的形式。 L eg acy 库 的主要 内容就是使用 了/dev/eac 虚拟设备文件进行音频操作 ,与 以
前的 A n dro id 版本没有 区别 。
audio_hw_hal.cpp 是硬件抽象层 的接 口文件 ,其 中几个 结构体 的定义如下所示 :
struct legacy_audio_module ( II audio_riiodule 的封装
struct audio module module;
};
s 七ruct legacy_audio_device ( // audio_hw_device 的封装
struct audio hw device device;
struct AudioHardwareinterface *hwif;
);
struct legacy ,stream
. . out ( // audio_stream_out 的封装
struct audio stream out stream;
AudioStreamOut *legacy_out;
};
struct legacy stream in (


II audio .stream
. in 的封装
struct audio stream in stream;
AudioStreamin *legacy_in;
1;

以上几个接口实际上将各个新版本的接口和旧版本的接口封装到了 个结构体 当中。
由此,当硬件抽象层的使用者调用新的接口 (audio_ h w _ device 等 )时 ,该实现就用 旧的接
口 (A u d io H ardw arelnterface ) 实现 。

�330
第 21 章 Android 4.x 的音频、视频系统 oo•

legacy_adev _open() 是模块打开函数 ,主要 内容片段如下所示 :


s t a t i c int l e g a c y _ a d e v _ o p e n ( c o n s t h w _ m o d u l e _ t * module, c o n s t c h a r * name,
h w d e v i c e t** device) {
S七ruc七legacy_audio_device *ladev;
int ret;
if (strcmp(name, A U D I O _ H A R D W A R E _ I N T E R F A C E } != 0) r e t u r n -EINVAL;
l a d e v = (struct l e g a c y a u d i o d e v i c e *)calloc(l, s i z e o f ( * l a d e v ) } ; //设备结构
if (! ladev) r e t u r n -ENOMEM;
ladev->device.common.tag = HARDWARE_DEVICE_TAG;
ladev->device.common.version = A U D I O D E V I C E API V E R S I O N 2 0;
ladev->device.common.module = c o n s 七 c a s t < h w m o d u l e t*>(module); //模块
ladev->device.common.close = legacy_adev_close; //各个函数指针的实现
l a d e v - > d e v i c e . i n i t _ c h e c k = adev_ini七_check;
l a d e v - > d e v i c e . s e t v o i c e v o l u m e = a d e v set v o i c e volume;
l a d e v - > d e v i c e . s e t m a s t e r v o l u m e = a d e v set m a s t e r volume;
ladev->device.get_master_volume = adev_get_master_volume;

l a d e v - > d e v i c e . s e t m o d e = a d e v set mode;


l a d e v - > d e v i c e . s e t m i c m u t e = a d e v set m i c mu七e;
ladev->device.get_mic_mute = adev_get_mic_mute;
ladev->device.se七_parameters = adev_set_parameters;

ladev->device.get_parameters = adev_get_parameters;

ladev->device.ge七_input_buffer_size = adev_get_input_buffer_size;

ladev->device.open_output_stream = adev_open_outpu七_stream;
ladev->device.close_output_stream = adev_close_output_stream;
ladev->device.open_input_stream = adev_open_input_stream;

ladev->device.close_input_stream = adev_close_input_stream;

l a d e v - > d e v i c e . d u m p = adev_dump;
l a d e v 今 h w i f = c r e a t e A u d i o H a r d w a r e (); //建立Legacy的硬件抽象层实现
II省略部分内容
*device = &ladev->device.common; //设备结构
r e t u r n 0;
//省略部分内容

其中 一 个 A udio 模块 中函数 的实现如下所示 :


s t a t i c int a d e v _ s e t _ v o i c e _ v o l u m e ( s t r u c t a u d i o _ h w _ d e v i c e *dev, float volume) {
s t r u c t l e g a c y _ a u d i o _ d e v i c e * l a d e v = to_ladev(dev);
re七urn l a d e v - > h w i f 今 s e tV o i c e V o l um e (v o l um e ) ; / / 调 用 L e g a c y 的 函 数

由此可见,新版本中将 C 语言 函数指针 的实现 ,转成 了调用 legacy 库所 实现 的 C ++ 类


的接口来完成,其目的是为了让旧的实现方式可以直接使用。

一提示: Android 4.x 的 A udio 硬件 抽 象层 的主要 实现 与 以前 版 本 并 无本质 变化 ,针


对具体硬件的 A ndroid 2.3 的 A udio 实现 也 可 以采 用类似 的 方 法封 装成新 的硬 件模 块

汕 21.2.3 A2DP 实现

蓝牙系统也与音频部分相关,其中 A 2D P 可 以提供播放音乐 的接 口,这部分 内容 由


audio.a2dp.default.so 库来实现 。也就是说在蓝牙方面 ,蓝牙音频从主要硬件抽象层 中分离

出来,成为 个独立 的模块 。

331�
•oO Android板级支持与硬件相关子系统

Android 4.0-4. l中,在extemal/bluetootb/bluez/audio/目录中。


Android 4.2中,在external/bluetooth/bluedroid/audio_ a2dp_ hw/目录中。
Android 4.0-4.1和Android 4.2的实现变化主要是由于蓝牙体系结构的变化引起的。
Android 4.2中不再使用BlueZ的用户空间库作为实现。

�21.2.4 Galaxy Nexus的实现


Galaxy Nexus在硬件上使用Panda板的硬件,由于设计了OM 战 》4 处理器之外 的 C odec
硬件,因此Audio部分的实现不仅仅与处理器相关,且属千板级实现。

1. 驱动程序部分
OMAP体系的处理器都使用标准的ALSA作为音频的驱动程序。
目标系统运行是在/dev/snd/目录中包含了ALSA各个字符设备文件,其中,第 一 个声
卡的总控节点是controlCO, 具 有 p c m C O D < N > c 和 p c m C O D < N > p , N的范围是0 17, 有
的同时有播放和捕获接口,有的只有播放接口;第二个声卡的总控节点是controlCl, 有
p c m C I D O p 一 个播放设备 。
按照ALSA系统的通常结构,在/proc/aound目录中也有相关的信息,devices文件也包
括了各个子设备的详细信息。cards文件的内容如下所示:
0 [Tuna ) : OMAP4 - Tuna
TI OMAP4 Board
1 (OMAP4HDMI

): OMAP4HDMI - OMAP4HDMI

第 一 个声卡是 T una 板 的主要音频 设备 ,第二个声卡是 O M 凡 >4 处理器 H D M I 输 出。


OM战系统的音频的实现路径为/sound/soc/omap。其中的sdp4430.c文件被编译成
snd-soc-sdp4430.o, 为主要声卡的实现,omap4-hd血-card.c文件被编译生成snd-soc-
omap4-h 如 ll.O , 为 H D M I (High Definition Multimedia Interface, 高清晰度多媒体接口)声
卡的实现。
sdp4430.c中定义了snd_soc_card类型的结构snd_soc—sdp4430, 如下所示:
static struct snd_soc_card snd_soc_sdp4430 = { //范义-;;;-d_soc_ rd 刁
咖 拉
.driver_name = "OMAP4",
.long_name = "TI OMAP4 Board",
.dai_link = sdp4430_dai,
.num_links = ARRAY_SIZE(sdp4430_dai),
.stream_event = sdp4430_stream_event,

snd_soc_c a r d 类 型 是 A L S A 系 统 的 soc 硬件实现 的接 口,其 中包含 了 snd_soc_dai_ link


类型的数组,表示与Codec方面的接口。
sdp4430.c中定义了snd_soc_card类型的结构snd_soc_sdp4430, 如下所示:
static struct snd_soc_dai_link sdp4430_dai[] = {
{ .name = "SDP4430 Media", //播放设备
.stream_name = "Multimedia", .cpu_dai_name = "MultiMedial",
.platform_name = "omap-pcm-audio", .dynamic= 1, /* BE is dynamic */
,-.,.,.,..,'·-
.ini 七 = _ sdp4430 twl6040 fe init, //初始化函数

�332
第21章 Android 4.x的音频、视频系统 ooe

.dsp_link = &fe_media,
)(

.name = "SDP4430 M e d i a C a p t u r e " ,

//捕获设备
.stream_name = " M u l t i m e d i a C a p t u r e " , .cpu_dai_name = " M u l t i M e d i a 2 " ,
.platform_name = "omap-pcm-audio",
. d y n a m i c = 1 , / * BE i s d y n a m i c * / .dsp_扛nk'= &fe_media_capture,
},
//省略其他设备的内容

此处定义的内容就是第 一 个 声卡 中的各个子设备 ,每个设备 占用 一 个 p (播放)或者c


(捕获)类型的设备节点。
同样,sdp4430.c文件中定义了如下结构:
s t a t i c s t r u c t s n d _ s o c _ c a r d snd_soc_omap4 hdmi = {
. n a m e = "OMAP4HDMI",
. d a i _ l i n k = &omap4_hdmi_dai, . n u m _ l i n k s = 1, //设备的名称
. c o n t r o l s = &hdmi_max_chan_ctl, . n u m _ c o n t r o l s = 1,
);

此 处 的 s n d_soc_c a r d 结 构 的 名 称 为 " O M 战 叩 叩 M I " , 也 就 是 第 二 个 声 卡 O M 凡) 的


HDMI音频实现。

2. 硬件抽象层部分
Galaxy Nexus的Audio部分的硬件抽象层使用tuna板的实现,其代码在板级支待目录
中,路径为device/samsung/tuna/audio/, 将生成audio.primary. tuna.so, 实现中也调用了 一 个
tinyalsa库。
Audio模块中的audio_hw.c, adev_open就是音频模块的打开方法,其片段如下所示:
static int a d e v _ o p e n ( c o n s t h w _ m o d u l e _ t * m o d u l e , c o n s t c h a r * name,
hw d e v i c e t * * d e v i c e ) {
s t r u c t tuna audio device *adev;
int ret;
if ( s t r c m p ( n a m e , AUDIO_HARDWARE_INTERFACE) ! = 0) r e t u r n -EINVAL;
adev = c a l l o c ( l , s i z e o f ( s t r u c t tuna_audio_device));

if ( ! a d e v ) r e t u r n -ENOMEM; / / tuna_audio_device au d i o_ hw_ d e v i c e





a d e v - > h w _ d e v i c e . c o m m o n . t a g = HARDWARE_DEVICE_TAG;
adev->hw device.common.version AUDIO DEVICE A P I VERSION 2 O;
//版本

=

adev->hw_device.common.module = ( s t r u c t hw_module t * ) m o d u l e ; // Audio


adev->hw device.common.close = adev c l o s e ;
/ / 关 闭 函 数 //分配混音

adev->hw_device.init_check = adev_init_check;
adev->hw_device.set_voice_volume = adev_set_voice_volume;
adev->hw d e v i c e . s e t m a s t e r volume = adev s e t m a s t e r volume;
adev->hw d e v i c e
//省略部分内

中的各个函数指针

a d e v - > m i x e r = m i x e r open(CARD_OMAP4_ABE);

If









adev->mixer_ctls.dll_eq = m i x e r g e t c t l b y name(adev->mixer,
MIXER D L l EQUALIZER);
adev->mixer_ctls

理器设备
/容











/:

pthread_mutex_lock(&adev->lock);
省略部分内容:

set_route_by_array(adev->mixer, defaults, l ) ;
a d e v - > m o d e = AUDIO MODE NORMAL; / / Audio




a d e v - > o u t _ d e v i c e = AUDIO_DEVICE_OUT_SPEAKER;
a d e v - > i n _ d e v i c e = , . j积 ,
p �Q D E V I C E _ I 应 B U I L T J . N _MIC J,_;;AUDIO _ D E V I C E _ B I T _ I N ;

333
•oo Android 板级支持与硬件相关子系统

select_output_device(adev); II设置输出设备
//省略部分内容
ril_open (&adev->ril); / / RIL相关的设贺内容
p t h r e a d mutex u n l o c k ( & a d e v - > l o c k ) ;
r i l _ r e g i s t e r _ s e t _ w b _ a m r _ c a l l b a c k ( a u d i o s e t wb a m r c a l l b a c k , ( v o i d * ) a d e v ) ;
* d e v i c e = &adev->hw_device.common; II用参数返回设备的结构
r e t u r n O;

此处实现的主体内容也就是 h w_d e v i c e 结构 的实现 ,t u n a_ a u d i o _ d e v i c e 实际上是


au d i o _

a u d i o _h w _ d e v i c e 的继承者 ,包含 了 T u n a 音频硬件抽象层 的额外信 息 。


特殊的地方是与电话部分相关的内容,需要通过调用 RI L 内容 ,此 部 分 内容 在
r i l _i n t e r f a c e . * 文件 中实现 ,包括设置音量 、设置音频路径等功能 ,通过动态打开 R IL 的客
户端库 l i b sec r i l - c l i en t .so 来实现 。
其中负责打开输出设备的函数 i n t a d e v_ o p e n _o u t p u t _ s t r e a m ( ) 的内容 如下所示 :

sta巨c int adev_open_output_stream(struct audio_hw_device *dev,


audio io handle七handle, audio devices t devices,
audio_output_flags_t flags, struct audio_config *config,
s t r u c t audio—stream_out **stream_out) {
s t r u c t tuna_audio_device *ladev = ( s t r u c t tuna_audio_device *)dev;
s t r u c t tuna_s七ream_out * o u t ; in七ret; int output_type;
* s t r e a m _ o u t = NULL;
out= ( s t r u c t t u n a _ s t r e a m _ o u t * ) c a l l o c ( l , s i z e o f ( s t r u c t tuna_stream_ou七));
if (!out) r e t u r n -ENOMEM;
o u t - > s u p c h a n n e l m a s k s [ O J = AUDIO CHANNEL OUT STEREO;
o u t - > c h a n n e l mask = AUDIO CHANNEL OUT STEREO;
if ( f l a g s & AUDIO OUTPUT FLAG DIRECT &&
d e v i c e s = = AUDIO DEVICE OUT AUX D I G I T A L ) {
II省略部分内容
r e t = out_read_hdmi_channel_masks(out); //打开HDMI设备
if ( r e t ! = 0) g o t o e r r open;
o u t p u t t y p e = OUTPUT_HDMI;
//省略部分内容
out->channel_mask = config->channel_mask;
out->strearn.common.get_buffer_size = out_get_buffer_size_hdrn让
o u t - ' > s t r e arn . c ommo n . g e t _ s amp l e_ r a 七e = out_get_sarnple_rate_hdrni;
out->strearn.ge七一latency = ou七_get_latency_hdrni;
o u t - > s t r e a r n . w r i t e = o u t w r i t e hdrni; / / HDMI音频设备的写实现
out->config[PCM_HDMI] = pcrn_config_hdrni_rnulti;
out->config[PCM_HDMI) . r a t e = config->sarnple_rate;
out->config[PCM_HDMI].channels = popcount(config->channel_rnask);
o u t - > r e s t a r t _ p e r i o d s _ c n t = out->config[PCM_HDMI) . p e r i o d _ c o u n t * 2;
) e l s e i f ( f l a g s & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) {
II省略部分内容
) else f
II省略部分内容:正常的Audio部分的实现
OU七put一七ype = OUTPUT_LOW_LATENCY;
out->stream.common.get_buffer_size = out_get_buffer_size_low_latency;
out->s七rearn.common.get_sample_rate = out_get_sarnple_rate;
out->stream.get_latency = out_get_latency_low_latency;
out->strearn.write = ou七_write_low_latency; II写的实现

ret = c r e a t e _ r e s a m p l e r ( D E F A U L T _ O U T _ S A M P L I N G _ R A T E , MM_FULL_POWER SAMPLING RATE,


2 , gES坚PLER_QUALITY_DEFAULT, NULL, & _ o u t - > r e s a m p l e r ) ; / / 建 立 项 取 样 器

�334
第21章 Android 4.x的音频、视频系统 ooe

i f ( r e t ! = 0) g o t o e r r _ o p e n ;
out->stream.common.set_sample_rate = out_set_sample_rate;
o u t 今 s t r e am. c ommo n . g e t _ c h a n ne l s = o u t _ g e t _ c h a n n e l s ;
II 省略 部分 内容 :o u t - > s t r e a m . c o m m o n 当中的函数指针 的实现和 ou t 今 江 r e am 其他成 员
*stream_out = &ou亡>stream; I I 返回 a u d i o s t r e a m o u t 类型的结构
ladev今outputs[output卫ype) = O U 七; //设置输出的类型

由于系统中不止 一 个 A udio 输 出,也就是主设备和 H D M I 的音频设备 ,因此需要根据


传入的flag类型选择不同的Audio设备。
用于普通输出设备的write实现的out_write_ low_latency()函数如下所示:
static s s i z e _ t out_write_low_latency(struc七audio_stream_out *stream,
const void* buffer, size_t bytes) {
int ret;
s t r u c t tuna_stream_out * o u t = (struc七tuna_stream_out *)stream;
s t r u c t t u n a a u d i o d e v i c e * a d e v = ou七 一 >d e v ;
s i z e _ t frame_size = audio_stream_frame_size(&out->stream.common);
s i z e _ t in_frames = b y t e s / frame_size;
s i z e _ t out_frames = in_frames; bool force_input_standby = false;
s t r u c t tuna stream i n * i n ; int i;
pthread_mutex_lock(&adev->lock);
p t h r e a d mutex l o c k ( & o u t - > l o c k ) ;
II 省略部分 内容 :在 s t a n d b y 时启动输 出设备
pthread_mutex_unlock(&adev->lock);
f o 工 ( i = 0 ; i < PCM_TOTAL; i + + J {
I * o n l y use resampler i f r e q u i r e d * I
i f ( o u t - > p c m ( i ] && ( o u 七 - > c o n f i g [ i ] . r a t e! = DEFAULT_OUT_SAMPLING_RATE)) {
o u t _ f r a m e s = ou七 一 >b u f f e r _ f r ame s ;
o u t 今 r e s amp l e r - > r e s amp l e_ f r om_ i np u t ( o u 七一 > r e s amp l e r , ( i n t 1 6 t * ) b u f f e r ,
釭n_frames, ( i n t 1 6 _ t *) out->buffer, &out_frames); I I 重取样
break;


//省略部分内容:回音 ( e c h o ) 的处理
for ( i = O; i < PCM_TOTAL; i + + ) { //利用循环进行写
if {out->pcm[i]) {
i f ( O U 七- > c o n f i g [ i ) . r a t e == DEFAULT_OUT_SAMPLING_RATE) { //根据采样率
r e t = PCM_WRITE(ou七->pcm [ i J , ( v o i d * ) b u f f e r , b y t e s ) ; / /直接写
) else {
r e t = PCM_WRITE(out->pcm[i), //考虑重取样的结果,然后再写
(void *)out->buffer, out_frames * frame_size);

if (ret) break;


//省略部分内容:退出之前的处理
return bytes; II 返回 :写 入字节 的数 目

用 于 进 行 写 操 作 的 P C M — W 阳 T E 宏被定义为 tinyalsa 中的函数 ,可能是用 内存 映射地


写 p c m _m m a p_write(), 也可能是直接写pcm_write()。

335�
• O o Android板级支持与硬件相关子系统

21.3 Android 4.x 照相机系统

�21.3.1 照相机系统的结构

Android 4.x的照相机系统的结构产生了 些变化 ,系统的结构如 图 2 1-2 所示 。
Java应用
...............................................................................................................................................

Java框架

Camera
API

图 21-2 Android 4.x 的照相机系统结构

照相机的硬件抽象层由原本固定链接的动态库,变成了Android中固定的硬件模块,
照相机服务的调用部分也有 一 些变化 ,Java 层之上 的部分基本不变 。

一提示:由于Android 4.0-4.1只有一种照相机硬件抽象层接口,Android 4.2增加了笫


二种照相机硬件抽象层接口,笫一种也可以使用。

�21.3.2 Camera的框架层
Camera4.x框架层的主要变化是CameraService, 因为要适应新的硬件抽象层,也就有

了实现上的 些 改变 。
Android 4.2的CameraService包括以下几个文件。
a CameraService. *: 照相机服务的核心实现,也就是实现了ICameraService。
碰 C am eraH ardw arelnterface.h: 对第 一 种硬件抽象层 的封装 。
CameraClient. *: 使用第 一 种硬件抽象层调用 的客户端 。
a Camera2Client. *: 使用第二种硬件抽象层调用的客户端。
a Camera2Device. *和camera2/*: 使用第二个硬件抽象层的内部实现。

在Andro过4.0和4.1的版本中,只需要支持 种硬件抽象层 ,因此与 A ndroid 2.3没有
本质区别,只是用CameraHardwarelnterface.h封装新的硬件抽象层模块实现。而在Android
4.2中,由于要支待两种硬件抽象层,才增加了Camera Client和Camera2Client等文件,作

�336
第21章 Android 4 . x 的 音 频 、 视 频 系 统 ooe

为两种硬件抽象层的实现。
Android 4.2 C a m e r a S e r v i c e 的 实 现 如 图 2 1 - 3 所 示 。

[Camera ICameraService

CameraService CameraService

CameraService.Client

camera2 中的
各个处理器

CameraH 釭 dw 釭 elnterface

camera2

图21-3 Android 4.x的CameraService结构

1. CameraService的第一种实现

C a m e r a H a r d w a r e l n t e r f a c e . h 中 定 义 了 类 , C a m e r a H a r d w a r e l n t e r f a c e 也 就 是 将 A n d r o i d 4.0
开始引入的Camera硬件模块形式,又转回了原本的Android 2.3之前的C++的接口形式。
由此,CameraClient. * 调 用 的 方 式 几 乎 和 A n d r o i d 2 . 3 没 有 区 别 。
CameraHardwarelnterface中的初始化过程如下所示:
camera device t *mDevice; //保存硬件抽象层设盈面苟丽
status_t initialize (hw_module_t *module) (
int rc = module->methods->open(module, mName.string(), / / 打 开 硬 件 模 块
(hw device t **) &mDevice);
//省略错误处理部分内容
initHalPreviewWindow(); //初始化硬件抽象层的各种回调函数
return re;


其中实现的 个 函 数 startP rev iew O 如 下 所 示 :
status_t startPreview() (
if (mDevice->ops 今 start_p rev iew ) //查看模块的函数指针
return mDevice->ops->start_preview(mDevice); II调用模块的接口
return INVALID_OPERATION;

实 际 上 , A n d r o i d 4 . 0 和 A n d r o i d 2.3的CameraHardwarelnterface.h文件只是名称类似,

功能并不相同。后者是 个 C 开 形 式 的类 , 由继 承 者 去 实 现 ;前 者 则 是 调 用 了模 块 形 式 的
C接口,由于封装成了C开的形式,让CameraService中旧的代码依然可以使用。

337�
• O O Android板级支持与硬件相关子系统

提示:与Audio用legacy封装的方式不同,Camera的Android 2.3的硬件抽象层,
并没有办法通过封装在Android 4.x中直接使用。

2. CameraService的第二种实现
如果想要使用第二个硬件抽象层,则需要使用Camera2Client. *、Camera2Device. *和
camera2/目录中的所有文件。本部分内容仅在Android 4.2中具有。
Camera2Device中对硬件抽象层进行调用的initialize()函数如下所示:
s t a t u s t C a m e r a 2 D e v i c e : : i n i t i a l i z e (camera module t *module) {
ATRACE C A L L ( ) ;
status t res;
char name[lO];
s n p r i n t f (name, s i z e o f ( n a m e ) , " % d " , m i d ) ;
camera2 d e v i c e t * d e v i c e ;
r e s = m o d u l e - > c o m m o n . m e t h o d s - > o p e n ( & m o d u l e - > c o m m o n , name, //打开模块
reinterpret_cast<hw_device_t**>(&device));
i f ( d e v i c e - > c o m m o n . v e r s i o n ! = CAMERA ,.DEVICE ' A P I VERSION 2 0) ( //检查版本
device->common.close(&device->common);
r e t u r n BAD VALUE;
I
// 省略部分内容

在硬件抽象层的打开过程中,还需要检查版本,如果硬件抽象层的版本不是第二个版
本的,将不再继续使用。Camera2Device中的NotificationListener则用于监听来自硬件抽象
层的事件。
camera2子目录中的CallbackProcessor、FrameProcessor、StreamingProcessor等类都是
处理器,也是线程的实现(Thread的继承者),它们用于在CameraService中的异步处理。
Camera2Client类实现了CameraService.Client, 由于第二个硬件抽象层和接口相差较大,
因此实现的内容比较多,主要调用了Camera2Device和各个处理器。
Camera2Client中的getCameraDeviceO返回的就是Camera2Device类,在startPreview()
函数执行的过程中,调用了startPreviewL()的过程,如下所示:
s t a t u s _ t C a m e r a 2 C l i e n t : : s t a r t P r e v i e w L { P a r a m e t e r s &params, b o o l r e s t a r t ) (
ATRACE C A L L ( ) ;
status_t res;
//省略部分内容,参数检查, Pr e v i e w 窗 口的准 备
r e s = mStreamingProcessor->updatePreviewStream{params); / / StreamingProcessor
//省略错误处理内容
Vector<uintB_t> outputStreams;
bool callbacksEnabled = params.previewCallbackFlags &
CAMERA FR扭E CALLBACK FLAG ENABLE MASK;
i f (callbacksEnabled) ( / / 在 C a l l b a c k P r o c e s s o r 中更新流
res = mCallbackProcessor->updateStream{params);
outputStreams.push{getCallbackStreamld{));

//省略部分内容
outputStreams.push(getPreviewStreamid());
i f ( ! p a r ams. r e c o r d i n g H i n t ) { / /判断录制的情况
i f ( ! r e s t a r t ) { r e s = mStreamingProcessor->updatePreviewReques七(params); I
r e s = m S t r e a m i n g P r o c e _ s s o r - > s t a r t S t r e a m ( S t r e a m i n g P r o c e s s o r : : PREVIEW,

�338
第21章 Android 4.x的音频、视频系统 ooe

outputStreams);
I else {
r e s = mJpegProcessor今updateStream(params);// 准备 J p e g Pr o c e s s o
// S t r e a m i n g P r o c e s s o r 录制的处理
if (!restart) {
r e s = mStreamingProcessor->updateRecordingRequest(params);


res= mStreamingProcessor->startStream(S七reamingProcessor: :RECORD,
OU七putStreams); / / 在 S 扛 e a m i n g P r o c e s s o r 中开始流

params.state = Parameters::PREVIE阳 //最后转为预览的状态


r e t u r n OK;

由于处理不同的场景,实现过程中参数的路径比较复杂,但主题内容都是通过用于回
调的CallbackProcessor和用于各种流处理的StrearningProcessor。每个处理器都调用了
getCameraDevi叫)得到Camera2Device, 调用下层的内容。由此对照相机各个环节的处理,
实际上被分散到各个处理器中。

�21.3.3 照相机BSP部分结构
Android 4.x照相机硬件抽象层的接口在libhardware/include/hardware/目录中,
camera_cornmon.h定义了公共内容,camera.h和camera2.h是两个版本的硬件抽象层接口。

1. 第 一 种接 口形式
camera.h是照相机硬件抽象层的第 一 种接 口,在 A ndroid 4.0的版本开始引入,此接口
与Android 2.3的本质相同,只是由原本的C丹类改为了C方式硬件模块。
视频数据流的预览由preview_stream_ops结构描述,如下所示:
t y p e d e f s t r u c t preview_stream_ops {
i n t ( * d e q u e u e _ b u f f e r ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s * w,
buffer_handle_七** b u f f e r , i n t * s t r i d e ) ;
i n t ( * e n q u e u e _ b u f f e r ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s * w, b u f f e r _ h a n d l e _ t * b u f f e r ) ;
i n t ( * o a n c e l _ b u f f e r ) ( s t r u c t p r e v i e w _ s 七 r e a m _ o p s * w, b u f f e r _ h a n d l e _ t * b u f f e r ) ;
i n t ( * s e t _ b u f f e r _ c o u n t ) ( s t r u c t p r e v i e w _ s 七 r e a m _ o p s * w, i n t coun七);
i n t ( * s e t _ b u f f e r s _ g e o m e t r y ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s * pw,
i n t w, i n t h , i n t f o r m a t ) ;
i n t ( * s e t _ c r o p ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s *w,
i n t l e f t , int七op, i n 七 r i g h t , i n t bottom);
i n t ( * s e t _ u s a g e ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s * w, i n t u s a g e ) ;
i n t ( * s e t _ s w a p _ i n t e r v a l ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s *w, i n t i n t e r v a l ) ;
i n t (*get_min_undequeued_buffer_coun七) ( c o n s t s t r u c t p r e v i e w _ s t r e a m _ o p s *w,
i n t *count);
i n t ( * l o c k b u f f e r ) ( s t r u c t p r e v i e w _ s t r e a m _ o p s * w, b u f f e r _ h a n d l e _ t * b u f f e r ) ;
i n t ( * s e t _ t i m e s t a m p ) ( s 七 r u c t p r e v i e w _ s t r e a m _ o p s *w, i n t 6 4 _ t t i m e s t a m p ) ;
} preview_stream_ops_t;

由于预览数据是照相机中重要的实现部分,因此作为 一 个单 独 定 义 的结 构 ,
dequeue_buffer、enqueue_buffer、cancel_buffer和lock_buffer是当中的核心实现,它们都是
针对Buffer内存的队列操作。
表示照相机设备操作的结构camera_device_ops如下所示:

339�
• O o Android 板级支持与硬件相关子系统

t y p e d e f s t r u c t camera_device_ops {
i n t (*set_preview_window) ( s t r u c t camera_device * ,
s t r u c t preview_stream_ops *window);
v o i d ( * s e t _ c a l l b a c k s ) ( s t r u c t c a m e r a _d e v i c e * , / /回调的设置
camera_notify_callback n o t i f y _ c b , camera_data_callback data_cb,
camera_data_timestamp_callback data_cb_timestamp,
camera_request_memory get_memory, v o i d * u s e r ) ;
v o i d (*enable_msg_七ype) ( s t r u c t c a m e r a _ d e v i c e * , i n t 3 2 _ t m s g _ t y p e ) ;
v o i d ( * d i s a b l e _ m s g _ 七 y p e ) ( s t r u c t c a m e r a _ d e v i c e * , i n t 3 2 _ t msg一七ype);
i n t (*msg_type_enabled) ( s t r u c t camera_device * , i n t 3 2 _ t msg_type);
i n t (*s七art_preview) (struc七camera d e v i c e * ) ; //预览相关
v o i d (*stop_preview) ( s t r u c t camera_device * ) ;
i n t (*preview_enabled) ( s t r u c t camera_device * ) ;
i n t ( * s t a r t r e c o r d i n g ) ( s t r u c t camera d e v i c e * ) ; //录制相关
v o i d (*stop_recording) ( s t r u c t camera_device * ) ;
i n t (*recording_enabled) ( s t r u c t camera_device * ) ;
v o i d (*release_recording_frame) ( s t r u c t camera_device * , c o n s t v o i d *opaque);
i n t (*auto focus) (struc七camera d e v i c e * ) ; //自动对焦相关
i n t (*cancel_auto_focus) ( s t r u c t camera_device * ) ;
i n 七 ( * t a k e _ p i c t u r e ) ( s t r u c t camera_device * ) ;
i n t ( * c a n c e l _ p i c t u r e ) ( s t r u c t camera_device * ) ; I I拍摄相关
i n t ( * s e t _ p a r a m e t e r s ) (s七ruct camera_device * , c o n s t c h a r *parms); II参数相关
c h a r *(*ge七_parameters) ( s t r u c t camera_device * ) ;
v o i d (*put__parame七ers) ( s t r u c t c a m e r a _ d e v i c e * , c h a r * ) ;
i n t (*send_command) ( s t r u c t c a m e r a _ d e v i c e * ,
i n t 3 2 _ t cmd, i n t 3 2 _ t a r g l , i n t 3 2 _ t a r g 2 ) ;
v o i d ( * r e l e a s e ) ( s t r u c t c a m e r a _d e v i c e * ) ;
i n t (*dump) ( s t r u c t c a m e r a _ d e v i c e * , i n t f d ) ;
} c a m e r a dev1.ce o p s t ;

c a m e r a _d e v i c e — o p s 中的结构与 以前 的版本类似 ,区别在于 set_ p r ev i ew _ w i n d o w 函数指


针使用了 p r ev i ew _ s t r e a m _o p s 作为参数 。
表示设备的结构 c am er a_ d e v i c e _t 是 h w _ d e v i c e _t 的继承者 ,c am er a_ d e v i c e _o p s _ t 是

c a m e r a _ d e v i c e _t 中的 个成 员。

2. 第二种接口形式
camera2.h 是照相机硬件抽象层 的第二种接 口,在 A ndro id 4 . 2 的版本 开始 引入 ,两种
接口形式在 A n dr oid 4.2 系统 中可 以共存 。
表示数据流操作的 c am er a 2 _ s t r e a m _o p s 结构 ,如下所示 :
t y p e d e f s t r u c t camera2_stream_ops {
i n 七 ( * d e q u e u e _ b u f f e r } ( c o n s t s t r u c t c a m e r a 2 _ s t r e a m _ o p s * w,
b u f f e r handle t * * b u f f e r ) ;
i n t ( * e n q u e u e _ b u f f e r } ( c o n s t s t r u c t c a m e r a 2 _ s t r e a m _ o p s * w,
int64_t七imestamp, buffer_handle_t* b u f f e r ) ;
i n t ( * c a n c e l _ b u f f e r ) ( c o n s t s t r u c t c a m e r a 2 _ s t r e a m _ o p s * w,
b u f f e r handle t * b u f f e r ) ;
i n t ( * s e t _ c r o p ) ( c o n s t s t r u c t c a m e r a 2 _ s t r e a m _ o p s *w,
int left, int top, in七right, int bottom};
} camera2 s t r e a m ops t ;

c a m e r a 2 _ s t r e a m _o p s 结构 中的内容表示对 B u f f er 内存 的队列操作 。这是 一 个需要硬件


抽象层的调用者实现的函数集合: e n q u e u e _b u f f e r 用于将一 个 B u f f er 加入 队列 ,
d e q u e u e _b u f f e r 用于从 队列 中取 出 B u f f er , cancel_buffer 则用于异步取 消操作 。

�340
第21章 Android 4 . x 的 音 频 、 视 频 系 统 oo•

in_ o p s 和 cam era2_ request_ q u e u e _src—


c a m e r a 2_ stream— op s 等 其 他 操 作 函 数 , 也 是 由 调
用者实现的。
表 示 照 相 机 的 主 要 操 作 c a m e r a 2_device_ o p s 结 构 如 下 所 示 :
typedef struct camera2_device_ops {
//申请、建立帧队列
int (*set request queue src ops) (const struct camera2 d e v i c e * , / / 输 入 的 队 列
const camera2_request_queue_src_ops_t *request_src_ops);
int (*notify_reques七_queue_not_empty) (c;:onst struct camera2 device *);
int (*set_frame_queue_dst_ops) (const struct camera2_device *, II 输 出帧 的操作
const camera2_frame_queue_ds七_ops_t *frame_dst_ops);
int (*get_in_progress_count) (const struct camera2_device *);
in七(*flush_captures_in_progress) (const struct camera2_device *);
int (*construct_default_request) (con t struct camera2_device *,
耳让request_template, camera_metadata_t **request);
//流的管理
int (*allocate_stream) ( c o n s t struct camera2_device *, II 分配 一 个固定大小的流
uin七32_t width, uint32_t height, int format, //用于输入的参数
const camera2_stream_ops_t *stream_ops,
uint32 t *stream id, uint32 t *format actual, II 用于输 出的参数
uin七32_t *usage, uint32_t *max_buffers);
int (*register_stream_buffers) ( c o n s t struct camera2_device *,II 为流注 册 Bu ffe r
uint32_t stream_id, int num_buffers, buffer_handle_t *buffers);
int (*release stream) ( c o n s t struct camera2 device *, I I 释放流
uint32 t stream_id);
//省略部分内容
int (*set_notify_callback) (const struct camera2_device *,
camera2_notify_callback notify_cb, void *user);

c a m e r a 2 _device_ o p s 中 使 用 比 较 抽 象 的 改 变 , 表 示 了 照 相 机 的 重 点 操 作 , 典 型 的 内 容
是表示内存的帧和表示数据的流,各种操作都使用结构体来表示。
表 示 通 知 的 c a m e r a 2_notify_ callback的格式如下所示:

typedef void (*camera2_notify_callback) (int32_t msg_type,


int32 t extl , _ int32_1: ext2, intl2 t ext3 J 立o .j.d__ *us_er);

msg_type参数表示回调的类型,由各个以CAMERA2_MSG _为开头的宏来表示。
表 示 设 备 的 c a m e r a 2_ d e v i c e 结 构 继 承 了 b w _ d e v i c e _ t , 并 且 包 括 了 c a m e r a 2_device_ o p s _ t
结构来实现操作。
调用者对硬件抽象层的操作流程如下:
o 在 照 相 机 模 块 打 开 之 后 , 进 行 简 单 的 初 始 化 , 调 用 s e t _ notify_c a l l b a c k 注 册 由 调 用
者 所 实 现 的 回 调 c a m e r a 2 _notify_ callback, 回调中处理快门、自动对焦和错误信
息等。
o 在 照 相 机 设 备 初 始 化 阶 段 就 调 用 a l l o c a t e _stream, 需 要 传 入 所 需 要 的 宽 和 高 , 然 后 将
调 用 者 实 现 的 流 操 作 c a m e r a 2_stream_ o p s _ t 作 为 参 数 传 入 , 返 回 后 得 到 流 的 过 。
o 调 用 者 根 据 流 的 心 调 用 r e g i s t e r _ stream_b u f f e r s 将 调 用 者 准 备 的 B u f f e r 注 册 给 照 相
机设备,这个Buffer就是传递数据的内存。
o 硬 件 抽 象 层 的 实 现 要 在 自 身 的 运 行 中 调 用 c a m e r a 2 _stream_o p s _t 中 的 函 数 , 将 通 过

341�
• O O Android板级支持与硬件相关子系统

Buffer数据传递给调用者,其中根据需要调用了回调。
在退出阶段,调用者调用release_stream根据流的闪释放,之后硬件抽象层不再对
Buffer进行操作。

提示:照相机的笫二个硬件抽象层的接口更注重队列的方式,而Camera Service之上
的接口依然是笫一种形式,因此CameraService需要为笫二个硬件抽象层做更多处理。

由千两种硬件抽象层可以并存,因此在Android 4.2的camera_common.h头文件中,使
用camera_info结构体中指定的版本信息。

21.4 Android 4 . x 照 相 机 的 B S P 实 现

汕21.4.1 仿真器实现

在Android 4.x的仿真器中,可以使用照相机实现预览和拍摄照片的功能,该硬件抽象
层在没有驱动程序的情况下实现。
照相机硬件抽象层代码路径为development/tools/emulator/system/camera/, 其中的内容
将生成动态库camera.goldfish.so, 放置在目标系统的/system/lib/bw目录中。
在Android 4.2中,由千包括cameral和camera2两种硬件抽象层接口。因此,对上层
需要实现两种接口(camera_device和camera2_device) , 而 下 层 由 两 种 设 备 ( F a k e 和 Q e m u )
来实现,因此有四种排列组合,几个类之间的继承关系比较复杂。
Android 4.2的照相机仿真器硬件抽象层的结构如图21-4所示。
camera aev,ce
(HAL接口)

EmulatedCameraHal
EmulatedCameraFactory
(HAL实现入口)

EmulatedBaseCamera
EmulatedCameraDevice

EmulaledCamera2

EmulaledFakeCameraDevice

图 21-4 Android 4.2 的照相机仿真器硬件抽 象层结构

仿真器硬件抽象层的实现主要包括以下文件。
e EmulatedBaseCameraHal.cpp: 照相机硬件抽象层的入口。
e EmulatedCameraFactory. *: 照相机的工厂类,被EmulatedBaseCameraHal调用。

342
第21章 Android 4.x 的音频 、视频系统 ooe

• EmulatedBaseCamera.* : 照相机的实现的基类。
• E m u l a t e d C a m e r a . * 和 E m u l at ed C am er a 2 . * : 分别为两种接口实现的基类。
• EmulatedFakeCameraDevice.* : 表示基于 F ak e 的 C am er a 设备 。
• EmulatedQemuCameraDevice.* : 表示基于 Q em u 的 C am er a 设备 。
• CallbackNotifier.* : 照相机回调功能。
• JpegCompressor. * : JPEG 文件 的生成功能 。
• PreviewWindow: 用于处理预览的窗口。

1. 全局部分
EmulatedBaseCameraHal.cpp 是整 个照相机硬 件抽 象层 的入 口,其 中定 义 的 c a m er a_

module_t 模块如下所示 :
c a m e r a m o d u l e t HAL ,MODULE

INFO SYM = {
common: {
tag: HARDWARE MODULE TAG,
m o d u l e a p i v e r s i o n : CAMERA MODULE A P I VERSION 2 0 ,
h a l a p i v e r s i o n : HARDWARE HAL A P I VERSION,
id: CAMERA HARDWARE MODULE I D ,
name: " E m u l a t e d Camera M o d u l e " ,
author: " T h e A n d r o i d Open S o u r c e P r o j e c t " ,
me七hods: &android::Emula七edCameraFactory::mCameraModuleMethods,
dso: NULL,
res,erved: (0)'


},
g e t number o f cameras: / /照相机相关内容
android::EmulatedCarneraFactory::get_nurnber_of_carneras,
get_camera_info: android::ErnulatedCameraFactory::get_carnera_info,
j..'

硬件抽象层入口直接调用的是 E m u l at ed C am er a F a c t o r y , EmulatedCameraFactory.cpp 中

通过 些属性 来判 断照相机 的使 用方式 ,例 如 ,g et B ac k C am er aH a l V e r s i o n ( ) 函数 根据读取
q e m u . s f . b a c k_ c a m e r a _h a l 属性来判断使用 F ak e 方式 的哪个版本 ,同样也可 以使用 Q em u 的
实现方式。
EmulatedCameraDevice 的基类 负责 了线程管理 的同步 ,类 定义 的接 口如下所 示 :
c l a s s EmulatedCameraDevice {
public:
v i r t u a l s t a t u s _ t c o n n e c 七 D e v i c e (} = 0 ;
v i r t u a l s t a t u s _ t disconnectDevice(} = 0;
virtual status_t startDevice(int width, int height, uint32_t pix_fmt} = O;
v i r t u a l s 七 a t u s t s t o p D e v i c e ( } = O;
// 省略部分内容
protec七ed:
virtual status t commonStartDevice(int width, in七height, uint32 t pix fmt};
// 省略部分内容
virtual bool inWorkerThread(};
II 省略部分内容

EmulatedCameraDevice 是 一 个实现公用功能的基类 ,有些 函数则需要让继承者来实现 ,


这个类自己也实现了 一 些 函数 ,可 以被继承者调用 。

343�
• O O Android板级支持与硬件相关子系统

一个内部的函数commonStartDevice()的实现片段如下所示:
status_t E m u l a t e d C a m e r a D e v i c e : : c o m m o n S t a r t D e v i c e ( i n t width, int height,
uint32 t p i x fmt) {
switch (pix_fmt) {
case V4L2 PIX FMT YVU420:
case V4L2 PIX FMT YUV420:
case V4L2 PIX FMT NV21:
case V 4 L 2 PIX FMT NV12:
m F r a m e B u f f e r S i z e = (width * height * 12) / 8; / / 计 算 B u f fe r 的大 小
break;
default:
return EINVAL;

m F r a m e W i d t h = width; m F r a m e H e i g h t = height; //保存信息


mPixelForrnat = pix_fmt; m T o t a l P i x e l s = w i d t h * height;
m C u r r e n t F r a m e = n e w uintB t[mFrameBufferSize); / / 建 立 Bu f fe r 的内存
return N O ERROR;

在EmulatedCameraDevice的继承者(Fake和Qemu的Camera设备)中,可以通过调
用commonStartDevice()来实现startDevice()。

2. 设备层
EmulatedFakeCameraDevice和EmulatedQemuCameraDevice是EmulatedCameraDevice
的两个继承者,它们也是设备层的实现。
EmulatedF akeCameraDevice中的inWorkerThread()是初始化的函数,又调用了
drawCheckerboard()、drawStripes()、drawSol叫)等绘制函数。
drawStripes()实际上就是完成绘制预览界面的函数,其实现的片段如下所示:
v o i d EmulatedFakeCameraDevice::drawStripes() {
const int c h a n g e _ c o l o r _ a 七 = m F r a m e H e i g h t / 4;
const int e a c h _ i n _ r o w = m U V I n R o w / mUVStep;
uintB_t* p Y = mCurrentFrame;
for (int y = 0; y < mFrameHeigh七; y++, p Y += mFrameWidth) {
YUVPixel* color;
c o n s 七 i n t c o l o r _ i n d e x = y / change_color_at;
if (color_index = = 0) { color = &mWhiteYUV; ) //敞上部的白色条
) else if (color_index = = 1) { c o l o r = &mRedYUV; ). //红色的条
) els e if (color_index = = 2) { color = &mGreenYUV;) //绿色的条
) else { color = &mBlueYUV; ) //最下面蓝色的条
changeWhiteBalance(color->Y, color 今 U , c o l o r 今 V ) ; //调用白平衡
memset(pY, changeExposure(color->Y), mFrameWidth); //清除所有亮度
const int u v _ o f f = (y / 2) * mUVInRow; / / 计 算 U V 平面在 内存的偏移
uint8_七* U = m F r a m e U + uv_off; / / 填 充 u 和 V 两个 平面
uintB_t* V = m F r a m e V + u v off;
-:-
for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
*U = color->U; *V = color->V; I I 设置每 一 个点的内存

各个drawXXX()调用的就是内存的操作,由于没有真正的硬件,此处得到的取景器等
数据都来自于软件的绘制,上面绘制的内容就是白、红、绿、蓝的色条。

344
第 21 章 Android 4.x 的音频、视频系统 ooe

EmulatedQemuCameraDevice 则是针对 Q em u 仿真器设备的实现 ,其 中的 st ar t D ev i c e ( )


的实现如下所示:
s t a t u s _ t EmulatedQemuCameraDevice::startDevice(int width, i n t height,
uint32_t pix_fmt) (
Mutex::Autolock locker(&mObjectLock);
// 省 略 错 误 处 理 的 内 容
s t a t u s _ 七 r e s = EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
m P r e v i e w F r a m e = new u i n t 3 2 _ t [ m T o t a l P i x e l s ] ; / / 建 立 预 览 的 内 存
// 省 略 错 误 处 理 的 内 容
r e s = mQemuClient. q u e r y S t a r t (mPixelFormat,, mFrame阳d吐,m�rameHeight);
if ( r e s = = NO_ERROR) {
r e i n 七 e r p r e t _ c a s t < c o n s t c h a r * > ( & m P i x e l F o r m a t ) , m F r a m e W i d t h , mFrameHeigh七);
mSta七e = ECDS STARTED; / /设置为开始的状态
) else {
reinterpret_cast<const char*>(&pix_fmt), width, height);

return res;

“ ”
s t a r t D e v i c e ( ) 调用 了基类 中的 c o m m o n St ar t D e v i c e( ) , 而涉及 硬件 的部分则需要 自身
的特定内容,其中调用的 Q em u C l i en t 就是照相机 的 Q em u 设备 的实现基础 。

3. 第 — 种接 口的实现
E m u l a t e dC a m e r a 提 供 了照 相 机 仿 真 器 硬 件 抽 象 层 第 一 种 实现 的主 要 内容 ,而
EmulatedFakeCamera 和 E m u l at ed Q em u C am er a 则 是 针 对 两 种 不 同 设 备 的 实 现 ,
E m u l a t e dC a m e r a 双继承 了 c am er a_ d e v i c e 和 E m u l at e d B a seC a m er a 两个类 ,如下所示 :
c l a s s EmulatedCamera : p u b l i c c a m e r a _ d e v i c e , p u b l i c EmulatedBaseCamera {)

在第 一 种接 口的实现 中,E m u l at e d C a m e r a 完成 了主要 的工作 ,只是对设备的适配部分


不同。其中 d o St ar t P r ev i ew ( ) 函数 的主干流程如下所示 :
S七atus_t E m u l a t e d C a m e r a : : d o S t a r t P r e v i e w ( ) {
E m u l a t e d C a m e r a D e v i c e * c a m e r a d e v = g e t C a m e r a D e v i c e )( ; II得到设备
II 省略错误处理的内容
status t res = mPreviewWindow.startPreview(); 心, II调用预览窗口的同名函数
// 省 略 错 误 处 理 的 内 容
int width, height;
i f ( m P a r a r n e 七 e r s . g e t ( C a m e r a P a r a m e 七 e r s : :KEY_VIDEO_SIZE) ! = NULL) {
mParameters.getVideoSize(&width, &height); II获取取景器预览的参数
) else {
mParameters. getP.reviewSize (&width, & h e i g h t ) ; '愤
//获取视频预览的参数

c o n s t c h a r * p i x _ f m 七 = NULL;
c o n s t c h a r * i s v i d e o = mParameters.ge七(EmulatedCamera::RECORDING_HINT_KEY);
// 省 略 像 素 格 式 参 数 方 面 处 理 的 内 容
r e s = camera d e v - > s t a r t D e v i c e ( w i d t h , h e i g h t , o r g _ f m t ) ; / / 调 用 设 备 的 开 始
II省略错误处理的内容
r e s = camera d e v - > s t a r t D e l i v e r i n g F r a m e s ( f a l s e ) ; //开始控制设备发送内存
// 省 略 错 误 处 理 的 内 容
return res;


d o S t a r t P r e v i e w ( ) 函数是 个典型 的处理流程 ,E m u l at ed C a m e r a 自身处理 了烦琐 的参数

345
• 0 0 Android板级支持与硬件相关子系统

处理内容,而对设备相关的操作,则调用设备层的函数完成。

4. 第二种接口的实现
EmulatedCamera2提供了照相机仿真器硬件抽象层第二种实现的主要内容,而由
EmulatedF a k e C a m e r a 2 和 E m u l a t e d Q e m u C a m e r a 2 则 是 针 对 两 种 不 同 设 备 的 实 现 ,
EmulatedCamera2双继承了camera2_ device和EmulatedBaseCamera如下所示:
class EmulatedCamera2._: publ_ic c;::amera2_device, public EmulatedBaseCamera { }

EmulatedCamera2中用于分配流的allocate_stream()函数如下所示:
int EmulatedCamera2::allocate_stream(const camera2_device_七*d,
uint32_t width, uint32_t height, int format,
const camera2_stream_ops_t *stream_ops, uint32_t *stream_id,
uint32_t *format_actual, uint32_t *usage, uint32_t *max_buffers) {
EmulatedCamera2* ec = getlnstance(d); //得到实际的设备
return ec->alloca七eStream(width, height, format, stream_ops, / / 设 备 层 函 数
stream_id, format_actual, usage, max_buffers);

此处得到的ec实际上是EmulatedCamera2的 一 个继承者 ,各种操作也通过设备层 的函


数完成,其他函数的实现也与之类似。
EmulatedF akeCamera2类是仿真器的主要实现,EmulatedQemuCamera2则是 一 个空的
实现,这两个类都是EmulatedCamera2的继承者。

5. 配置文件
仿真器的实现中还包括了的配置文件,media_codecs.xml和media_profiles.xml两个
XML格式放置在目标系统的/system/etc/目录中,
media_codecs.xml用于选择编解码器,me山a_profiles.xml用于确定 一 些可配置的参数 。
各个系统不同的照相机实现也可以用类似的方式配置参数。

�21.4.2 Galaxy Nexus的实现


OM 战 4 处理器的照相机具有后置摄像 头为 500 万像素 (2592x936 )、1080p@ 24fps 的
视频,支待自动对焦;前置摄像头为130万像素(1280x720)、720p@30fps的视频。

1. 全局部分
Galaxy N e x u s 的 照 相 机 系 统 O M 凡 >4 有 处理 器 统 一 的硬 件 抽 象 层 ,代码 的路 径 为
hardware/omap4xxx/camera/, 生 成 c a m e r a . o m a p 4 . s o 动 态 库 , 放 置 在 目 标 文 件 系 统 的
/system/lib/hw/目录中。
该硬件抽象层支持两种实现: 一 种是 O M X 的实现 ;另一 种是 V 4L 的实现 。G alaxy Nexus
使用了前者,在Android.rnk文件中使用OMAP4_C A M E R A _ H A L _USES宏作为区分。
OMAP4照相机的硬件抽象层通过C扞的实现,实现C形式的接口,包括的文件比较
多。头文件统 一 放置在 inc 目录 中,且结构和源代码 目录相 同。核心 内容如下所示 。
e CameraHal_Module.cpp: 通过camera_device_t实现了硬件抽象层的模块。

346
第21章 Android 4 . x 的 音 频 、 视 频 系 统 ooe

e CameraHal.cpp: 通 过 调 用 C a m e r a A d a p t e r 类 实 现 照 相 机 实 际 的 通 过 。
e AppCallbackNotifier.cpp: 对 于 上 层 回 调 的 封 装 。
e NV12_resize.c: 实现YlN格式缩放的工具。
e Encoder_libjpeg.cpp: 实 现 J p e g 的 软 件 编 码 ( 包 括 E X I F ) 的 工 具 。
e MemoryManager.cpp: 用 于 内 存 的 管 理 和 分 配 。
e inc/CameraHal.h: 硬 件 抽 象 层 内 部 实 现 的 头 文 件 , 定 义 众 多 的 类 。
O M X C a m e r a A d a p t e r 和 V4 L C a m e r a A d a p t e r 两 个 子 目 录 中 的 内 容 , 则 为 适 配 器 的 实 现 ,
通过继承CameraAdapter来完成。
OMAP4照相机的硬件抽象层在软件上分成两个层次:最上层的是对Android硬件抽象
层 接 口 的 实 现 , 以 c a m e r a—
d ev ice_ t 为 接 口 ; 下 层 是 适 配 器 的 实 现 , 以 C am eraA d ap ter 为 接
口;上层的CameraHal调用了下层的CameraAdapter。
C a m e r a H a l _M o d u l e . c p p 中 使 用 第 一 种 硬 件 抽 象 层 的 接 口 cam era_ device_t, 使 用 结 构
ti_camera—

d ev ice 作 为 封 装 , 增 加 个 成 员 表 示 不 同 照 相 机 设 备 的 id , 如下所示:
typedefs七rue七ti_camera_device {
camera device t base;
int cameraid; //表示照相机的过: 0为后置,1为前置
} ti camera device t;

硬 件 抽 象 层 的 c a m e r a _device_t 中 的 每 个 函 数 的 实 现 都 是 通 过 封 装 了 下 层 的 内 容 。例
如 , 控 制 回 调 的 c a m e r a—
en ab le_ m s g _type()函数如下所示:
static android::CameraHal* gCame 工aHals [MAX CAMERAS SUPPORTED]; //为?—(后置和前置)
void camera enable msg type(s七ruct camera device * device, int32 t msg type) {
七i came 工a device t* ti dev = NULL;
if (! device) 工etu 工n ;
豆_dev = {七i_camera_device_t*) device;
gCameraHals[ti_dev->cameraid)->enableMsgType{msg_type); //根据id选择内容

由此可见,硬件抽象层实际的实现者是CameraHal, CameraHal在CameraHal.h中定义,
在CameraHal.cpp中实现。
用于停止视频录制的stopRecordin队)函数如下所示:
void CameraHal::stopRecording() {
CameraAdapter::Adap七erS七ate currentState;
Mutex: :Autolock lock (mLock);
if (!mRecordingEnabled) { return; }
currentState = mCameraAdapter 今 getState () ; / / mCameraAdapter类型为CameraAdapter
if (currentState == CameraAdapter::VIDEO CAPTURE STATE) { //查看当前状态
mCameraAdapter->sendCommand(CameraAdapter::CAMERA STOP IMAGE CAPTURE);

mAppCallbackNotifier->stopRecording(); //调用对上层的回调
mCameraAdapter->sendCommand(CameraAdapter::CAMERA STOP VIDEO); //调用适配器
mRecordingEnabled = false;
if (mAppCallbackNotifier->getUesVideoBuffers ( ) ) {
freeVideoBufs(mVideoBufs);
if (mVideoBufs) { delete [) mVideoBufs; }
mVideoBufs = NULL;

mParameters.remove(TICameraParameters::KEY RECORDING, 一HINT); //对参数处理

347�
•oo Android 板级支持与硬件相关子系统

CameraHal 类本身负责 了状态管理 、参数处理 、回调等 内容 。而各个功能 函数 的实现


就是调用了真正的实现者 C am eraA dapter, 统 一 以 sendCommand() 函数为接 口。
用于释放视频帧的 releaseR ecordingFram e()函数如下所示 :
void CameraHal::releaseRecordingFrame(const v o i d * mem) {
if ( ( m R e c o r d i n g E n a b l e d ) && m e m != NULL) {
咄ppCallbackNotifier->releaseRecordingFrame{mem); //调用上层的回调

return;

此处的处理就不需要再调用 C am eraA dapter 类 ,只需要调用上层传入 回调 函数指针 。


AppCallbackNotifier 类用于对上层 回调的封装 ,包括通知的功能和帧数据 的传输 。

2. 适配器的实现
适配器类被 C am eraH al 类所调用 ,通过调用硬件相关 的 内容实现 :CameraAdapter 为
照相机核心功能的适配器,而 D isplayAdapter 用 于取 景器预 览显示 的适配 器 ,它 们都在
CameraHal.b 文件 中定义 。
照相机核心功能适配器 C am eraA dapter 的声 明如下所示 :
c l a s s C a m e r a A d a p t e r : p u b l i c FrameNotifier, p u b l i c vi工七ual R e f B a s e {
e n u m C a m e r a C o m m a n d s { 川 省略:CAMERA_START_PREVIEW等各种消息};
//省略其他函数的定义
v i r t u a l v o i d e n a b l e M s g T y p e ( i n t 3 2 _ t msgs, frame c a l l b a c k c a l l b a c k = NULL,
e v e n t _ c a l l b a c k e v e n t C b = N U 五 , v o i d * c o o k i e = NULL) = 0;
v i r t u a l v o i d d i s a b l e M s g T y p e ( i n t 3 2 _ t msgs, v o i d * cookie) = O;
v i r t u a l v o i d r e t u r n F r a m e ( v o i d * frameBuf, C a m e r a F r a m e : : F r a m e T y p e frameType) = O;
v i r t u a l v o i d a d d F r a m e P o i n t e r s ( v o i d *frameBuf, v o i d *buf) = 0;
virtual v o i d removeFramePointers() = O;
v i r t u a l s t a t u s _ t s e n d C o m m a n d ( C a m e r a C o m m a n d s operation,
int valuel=O, int value2=0, int value3=0) = 0;

CameraAdapter 类 中没有很 多函数 ,除 了设置回调 的几个 函数之外 ,照相机 的各个控制


功能都是通过 sendCommand() 函数完成 ,以命令为参数 ,C am eraC om m ands 枚举 的各个常
量表示发送的各种命令。 B aseC am eraA dapter 继承 了 C am eraA dapter。
显示适配器 D isplayAdapter 的声 明如下所示 :
c l a s s D i s p l a y A d a p t e r : p u b l i c BufferProvider, p u b l i c v i r t u a l R e f B a s e {
v i r t u a l i n 七 s e t P r e v i e w W i n d o w ( s t r u c t p r e v i e w _ s t r e a m _ o p s *window) = O;
v i r t u a l int s e t F r a m e P r o v i d e r ( F r a m e N o t i f i e r *frameProvider) = 0;
v i r t u a l int s e t E r r o r H a n d l e r ( E r r o r N o t i f i e r *errorNotifier) = O;
v i r t u a l int e n a b l e D i s p l a y ( i n t width, int height,
s t r u c t t i m e v a l * r e f T i m e = NULL, S 3 D P a r a m e t e r s * s 3 d P a r a m s = NULL) = O;
v i r t u a l int d i s a b l e D i s p l a y ( b o o l c a n c e l _ b u f f e r = true) = O;

Display Adapter 类用于照相机取景器 的预览 ,主要 函数是 setPreview W indow (), 以预览
的回调函数操作为参数。
CameraHal 的 setPreview W indow ()函数就调用 了 D isplayAdapter 的 setPreview W indow ()

348
第21章 A n d r o i d 4.x 的音频、视频系统 ooe

准备开始预览。
V 4LCameraAdapter 使用 了 V id e o F o r L i n u x 的驱动实现 ,以/d ev /v id e o 4 文件 为照相机 的
设备节点。帧控制方面的函数如下所示:
status_t V 4 L C a m e r a A d a p t e r : : f i l l T h i s B u f f e r ( v o i d * frameBuf,
CameraFrame: :FrameType frame Type) {
sta七us t ret = N O ERROR;
if ( ! m V i d e o i n f o - > i s S t r e a m i n g ) { return N O _ E R R O R ; )
i n t i = mPreviewBufs.valueFor((unsigned int)frameBuf);
if(i<O) { return BAD_VALUE; }
m V i d e o i n f o - > b u f . i n d e x = i;
m V i d e o i n f o - > b u f . t y p e = V 4 L 2 B U F TYPE V I D E O CAPTURE;
m V i d e o i n f o - > b u f . m e m o r y = V 4 L 2 M E M O R Y MMAP;
ret = ioctl(mCameraHandle, VIDIOC_QBUF, &mVideoinfo->buf); //将帧加入队列
if (ret < 0) { return -1; J
nQueued++;
re七urn ret;

c h a r * V4LCameraAdapter::GetFrame(int釭ndex} (
int ret;
m V i d e o i n f o - > b u f . t y p e = V 4 L 2 B U F TYPE V I D E O CAPTURE;
m V i d e o i n f o - > b u f . m e m o r y = V4L2_MEMORY_MMAP;
ret = ioctl(mCameraHandle, V I D I O C DQBUF, &mVideoinfo->buf); II从队列取出帧
if (ret < 0) { return N U L L ; )
nDequeued++;
index = mVideoinfo->buf.index;
re七urn (char *)mVideoinfo->mem[mVideoinfo->buf.index];

fillThisBufti叫 )
和 G etF ram eO 函数分别调用 了帧加入 队列和取 出的 io ctl, 并且此处使用
的帧是 V 4 L 2_ M E M O R Y _ M M A P , 也就是来自于 V 4L 2 驱动在 内核 中分配 的 内存 。

OMXCameraAdapter 是另外 的 种通过 O p en M a x 的实现 ,不直接调用驱动程序 ,而是
调用 O p en M ax 的接 口。例如 ,其 中的 startP re v iew () 函数如下所示 :

for(int index = O ;in d e x < mPreviewDa七a->mMaxQueueable;index++)


e E r r o r = OMX_FillThisBuffer(mCameraAdapterParameters.rnHandleCornp,
(OMX_BUFFERHEADERTYPE*)rnPreviewData->mBufferHeader[index]);
mFramesWithDucati++;
G O T O EX I T IF ((eError != O M X ErrorNone), eError);

OpenMax 实现 中还有几个文件 :O M X 3 A . c p p 表示 3A C 自动对焦 A u to f o cu s 、 自动增


强 A u to E n h a n c e 、 自动 臼平衡 A u to W h i t e B a l a n c e ) 功能的处理 ;O M 兄 \ l g o . c p p 用 千算法 的
处理; OMXCapture.cpp 用于拍照时 图像处理 ;O M 劝 沁 f . c p p 用于 Jp eg 文件 的 E x if 头信息
处理; OMXFocus.cpp 用于对焦处理 ;O M X Z o o m . c p p 用于缩放 处理 。

21.5 A n d r o i d 4.x 视频组合系统

�21.5.1 视频组合系统结构
A n d r o i d 4.x 中有 一 个名为 h w c o m p o se r 的新增硬件模块 ,作为视频数据 叠加输 出的支

349�
•oo Android板级支持与硬件相关子系统

持接口,其结构如图21-5所示。

椒戏.80 SurfaceFlinger
lbsurf8C41ffinger _c淑 邓 EGLD+splay EGL 守rface
HWComposer

芒言言·兰
本地框架

Linux 内核层

图21-5
l
Android 4.x的hwcomposer系统结构

视频组合依然采用了硬件模块的形式,并被SurfaceFlinger所调用。按照这个实现方式,
并不需要再使用以前版本主动调用的方式,而是根据hwcomposer硬件抽象层的库是否存在
来进行不同的操作。
hwcomposer与以前的overlay模块不同,overlay是针对硬件显示图层,提供数据通道

功能,而hwcomposer则与OpenGL的显示部分联系在 起 。这 种 变 化 与 硬 件 的 发 展 趋 势 有

关,在比较新的处理器和系统中,开始统 使 用 OpenGL 作 为 显 示 单 元 。
hwcomposer的调用者在surfacef l i n g e r 的 D i s p l a y Hardware中。
e frameworks/base/services/surfaceflinger/DisplayHardware/: o p e n g l 的 本 地 部 分 还 包 含
了hwcoposer的测试程序。
e frameworks/base/opengl/tests/hwc/: 其中的内容将生成静态库libhwcTest.a、hwcRects
等可执行程序。

提 示 : 在 A n d r o i d 4 . 2 系 统 中 , 相 应 代 码 路 径 均 为 : frameworks/native。

�21 .5.2 SurfaceFlinger对视频组合的使用


SurfaceFlinger的DisplayHardware目录中的HWComposer.cpp文件负责打开hwcoposer
硬件模块,如下所示:
void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
mDpy = {hwc display t)dpy; mSur = {hwc surface t ) s u r ; //设置句柄

status_t HWComposer::commit() cons 七 {


i n t e r r = mHwc->set(mHwc, mDpy, mSur, mList);
i f (mLis 切 { mList->flags &= -HWC_GEOMETRY_CHANGED; } //设置标志
re 七ur n ( s t a t u s _ t ) e r r ;

s e t F r a m e B u f f e r O 函 数 负 责 接 受 外 部 设 置 的 E G L D i s p l a y 和 E G L S u r f a c e , 而在comm戊)
函数调用硬件模块的接口将其设置到其中,它们将作为HWComposer的显示层。
Display H a r d w a r e 类 的 构 造 函 数 中 , 完 成 了 H W C o m p o s e r 类 的 初 始 化 和 设 置 显 示 , 其 中

�350
第21章 Android 4.x的音频、视频系统 ooe

的getHwComposer()函数返回HWComposer类型的指针。
SurfaceFlinger中的composeSurfaces()函数负责组合层,如下所示:
v o i d S u r f a c e F l i n g e r : : c o m p o s e S u r f a c e s ( c o n s t Region& dirty) {
const DisplayHardware& hw(graphicPlane(O) .displayHardwa 工e ());
HWComposer& hwc(hw.getHwComposer());
c o n s t size t fbLayerCount = hwc.getLayerCount(HWC_FRAMEBUFFER);
// 省略错误处理
hwc_layer_t* const cur (hwc. getLayers ());
cons七Vee七or< sp<LayerBase> >& layers (mVisibleLayersSortedByZ);
size_t c o u n t = layers.size();
for (size_t i=O ; i<count ; i++) { //处理各个层的情况
if (cur && (cur(i] .compositionType 1= HWC_FRAMEBUFFER)) {continue;}
c o n s 七 s p < L a y e r B a s e > & layer(layers[i]);
const Region clip(dirty.intersect(layer->visibleRegionScreen));
if (!clip.isEmpty()) { layer->draw(clip); } //进行绘制

在SurfaceFlinger的循环线程中,调用composeSurfacesO进行层的组合。这其实就构成
了Android 4.x显示系统对视频组合的内部处理。

�21.5.3 视频组合BSP部分结构
视频组合的代码路径在libhardware/include/hardware/目录中,hwcoposer.h是视频组合
的主要接口,hwcomposer_ defs .h则是 一 个相关的头文件 。H W C 本身具有 1.0 、1.1 和 1.2
几个不同的版本,它们内部处理的逻辑略有不同。
bwc_composer_ device_t是hw_device_t的继承者,其内容如所示:
typedef void* hwc一中splay_t; II 典型情 况 :E G L D i s p l a y
typedef void* hwc_surface_t; II 典型 情况 :EGLSurface
typedef struct h w c _ c o m p o s e r _ d e v i c e {
struct h w d e v i c e t common;
int (*prepare) (struct h w c _ c o m p o s e r _ d e v i c e *dev, hwc_layer_list_t* list);
int (*set) (struct h w c _ c o m p o s e r _ d e v i c e *dev, hwc_display_t dpy,
hwc_surface—t sur, hwc_layer_list_t* lis七);
v o i d (*dump) (struct hwc_composer_device* dev, char *buff, int buff_len);
int (*eventControl) (s七ruct hwc_composer_device_l* dev, int disp,
int event, int enabled);
in七(*blank) (struct hwc_composer_device_l* dev, in七disp, int blank);
int (*query) (struc七hwc_composer_device* dev, int what, int* value);
v o i d (*registerProcs) (struct hwc_composer_device* dev,
hwc_procs_t const* procs);
void* reserved_proc[4];
h w c me七hods t const *methods;
} hwc_composer_device_t;

此处使用的概念包括显示和图层,一般情况下,显示部分是EGLDisplay, 而图层部分
是EGLSurface。视频组合的核心内容是层。
几个主要函数指针的功能如下所示。
e prepare: 在准备每个帧之前调用,例如窗口建立、大小变化等,以层的列表为参数。
e set: 在交换内存之前的设置,返回后表示帧即将显示。
e query: 查询内存,在参数中返回信息。

351�
• O o Android板级支持与硬件相关子系统

e registerProcs: 注册回调函数,其中包括刷新,垂直同步和热插拔。
e eventControl: 控制事件的使能和禁止。
e blank: 关闭屏幕。
另外儿个主要的结构: hwc_layer_t表示 一 个硬件组合层 ;hwc_ layer_list_t表示层的列
表; h w c _procs_ t表示回调函数的句柄。

21.6 Android 4.x 视频组合的 B SP 实现

�21.6.1 默认实现
视频组合的硬件抽象层的默认实现在libhardware/modules/hwcomposer目录中,生成动
态库hwcomposer.default.so。由于不具有真正的硬件输出设备,这个库基本上是 一 个空的实
现,没有实际的功能。

�21.6.2 Galaxy Nexus的视频组合


Galaxy Nexus使用的OM战凶处理器的显示子系统,具有单独的显示单元支持叠加层,
使用dsscomp驱动程序和hwc的硬件抽象层。

1. 驱动程序部分
OM战处理器的显示子系统的组合(DSS Composition)功能支持代码路径为:

如vers/video/omap2/dsscomp/。这是 个 比较特 殊 的显示驱动 ,具有实现层和对用户空 间的
接口,这个显示层驱动不能与V4L2和FrameBuffer驱动同时存在。
include/video/目录中的头文件dsscomp.h定义了特定的ioctl接口,如下所示:
#define DSSCIOC_SETUP_MGR _IOW ( ' O ' , 128, struct dsscomp_setup_mgr_da七a)
#define DSSCIOC_CHECK_OVL _IOWR ( ' O ' , 129, struct dsscomp_check_ovl_data)
#define DSSCIOC_WB_COPY_IOW('O', 130, struct dsscomp_wb_copy_data)
#define DSSCIOC_QUERY_DISPLAY _IOWR('O', 131, struct dsscomp_display_info)
#define DSSCIOC WAIT I O W ( ' O ' , 132, struct d s s c o m p w a i t data)
#define DSSCIOC_SETUP_DISPC _IOW ( ' O ' , 133, struct dsscomp_setup_dispc_data)
#defin_e DS_SCIOC_SETUf_DISPLAY _ I O W ( ' Q ' , 134, str且吐斗S电comp_setup_display_data)

D S S C I O C_ W B _ C O P Y 用 于 写 回 方 式 的 内 存 复 制 , D S S C I O C _W A I T 用 于 等 待 , 另 外 还
有几个用于设置的命令。
驱动实现的device.c文件中注册了MISC字符设备节点,并调用其他内容实现了各种
ioctl命令,提供成用户空间的/dev/dsscomp设备节点。

2. 硬件抽象层部分
O M 战 处 理 器 显 示 部 分 的 硬 件 抽 象 层 代 码 路 径 为 : hardware/omap4xxx/hwc/, 生成
hwc.omap4.so动态库,放置在目标文件系统的/system/lib/hw目录中。这个实现中调用多种
硬件,包括了使用OMAP4显示子系统的组合设备dsscomp, 使 用 S G X O p e n G L 硬 件 , 调
用FrameBuffer的操作。

�352
第 21 章 A n d r o i d 4.x 的音频、视频系统 ooe

HWC模块的打开方法为:
static int omap4 h w c device open(const h w m o d u l e t* module, const char* name,
hw_device_t** device) {
o m a p 4 _ h w c _ m o d u l e _ t *hwc_mod = (omap4_hwc_module_t *)module;
o m a p 4 _ h w c _ d e v i c e _ t *hwc_dev;
int e r r = 0;
if (strcmp(name, HWC_HARDWARE_COMPOSER)) { return -EINVAL; }
if (! hwc m o d - > f b dev) {
err = omap4 h w c open fb h a l ( & h w c m o d - > f b dev); II 得到 FB 的文件描述符
if (err) re七urn err;
if (! h w c m o d - > f b dev) { return -EFAOLT; }
h w c _ m o d - > f b _ d e v - > b B y p a s s P o s t = l;

h w c _ d e v = (omap4_hwc_device_t *)malloc(sizeof(*hwc_dev));
if (hwc_dev = = NULL) return -ENOMEM;
memset(hwc_dev, 0, sizeof(*hwc_dev));
h w c _ d e v - > b a s e . c o m m o n . t a g = HARDWARE_DEVICE_TAG;
h w c d e v - > b a s e . c o m m o n . v e r s i o n = H W C DEVICE A P I V E R S I O N 1 0; II使用1.0版本
h w c _ d e v - > b a s e . c o m m o n . m o d u l e = (hw_module_t *)module;
h w c _ d e v - > b a s e . c o m m o n . c l o s e = omap4_hwc_device_close;
h w c _ d e v - > b a s e . p r e p a r e = omap4_hwc_prepare;
h w c _ d e v 今 b a s e .se t = omap4_hwc_set;
h w c dev-> b a s e . e v e n t C o n t r o l = omap4 hwc event control;
h w c _ d e v - > b a s e . b l a n k = omap4_hwc_blank;
h w c _ d e v - > b a s e . q u e r y = omap4_hwc_query;
h w c _ d e v - > b a s e . r e g i s t e r P r o c s = omap4_hwc_registerProcs; //回调的句柄
h w c _ d e v - > b a s e . d u m p = omap4_hwc_dump;
h w c d e v - > f b d e v = h w c m o d - > f b dev;
*device = &hwc_dev 今 b a se .com m o n ;
h w c d e v - > d s s c o m p f d = open("/dev/dsscomp", O_R,DWR); //打开显示组合的设备
if (hwc_dev->dsscomp_fd < 0) { e r r = -errno; g o t o d o n e ; )
h w c _ d e v - > h d m i _ f b _ f d = open("ldev/graphicslfbl", O_RDWR); II打开HDMI的FB
if (hwc dev->hdmi fb fd < 0) { e r r = -errno; g o t o d o n e ; )
h w c _ d e v - > f b _ f d = open("ldevlgraphics/fbO", O_RDWR);

在硬件模块打开时,需要打开各种需要使用的硬件设备: d s s c o m p , H D M I 的 F ram e B u ffer


的驱动,fb—d ev 则是从 g ra llo c 模块 中得到 的 F ra m e B u ffer 设备的文件描述符 。这里其实有

个隐含条件 ,就是 h w c 模块和 g ra llo c 都在 su rfa ceflin g er 的进程 中,因此表示进程打开文
件的文件描述符可以直接使用,并且 F ram eB u ffer 设备其 实只被打 开 了一 次 。
o m a p 4 _ h w c _prepare() 函数 的主体部分如下所示 :

static int o m a p 4 _ h w c _ p r e p a r e ( s t r u c t hwc_composer_device_l *dev, size_t numDisplays,


h w c d i s p l a y contents 1 t** displays) {
if (! n u m D i s p l a y s I I displays = = NULL) { return O; }
hwc_display_contents_l一七* list = displays [OJ; //忽略最上面的显示层
omap4 h w c d e v i c e t *hwc d e v = (omap4 hwc d e v i c e 七 * ) dev;
struct d s s c o m p _ s e t u p _ d i s p c _ d a t a *dsscomp = &hwc_dev->dsscomp_data;
struct counts n u m = { .composited_layers = l i s t ? list->numHwLayers : O };
u n s i g n e d i n t i , ix;
pthread_mutex_lock(&hwc_dev->lock);
memse七(dsscomp, OxO, sizeof(*dsscomp)); //线程的互斥上锁
d s s c o m p - > s y n c _ i d = sync_id++;
gather_layer_sta七istics (hwc_dev, &num, list);
decide_suppor七ed_cloning(hwc_dev, &num);
if (hwc_q.ev->force_sgx & & num. composi ted__layers < = 1)

353�
•oo Android板级支持与硬件相关子系统

hwc_dev->force—sgx = 0; //如果只有 一 个层,则要禁止 SG X


1.f (can_dss_render_all(hwc_dev, &num)) { //不使用SGX的情况
h w c _ d e v - > u s e _ s g x = 0;
h w c _ d e v - > s w a p _ r b = n u m . B G R != O;
} else { //在3个硬件的显示层之后使用SGX
h w c _ d e v 夕 u se_ sg x = l;
h w c _ d e v - > s w a p _ r b = is_BGR_format(hwc_dev->fb_dev->base.format);

II设置所使用的管道和DSS的层
d s s c o m p - > n u m _ o v l s = hwc_dev 今 u se_ sg x ; . / I设置所使用的管道
int z = 0; int fb_z = -1; int scaled_gfx = 0; int ix_docking = -1;
u n s i g n e d int m e m _ u s e d = O; hwc—dev->ovls_blending = 0;
for (i = O; list && i < list->numHwLayers; i++) { I I循环对列表的层进行操作
hwc_lay e r _ l _ t *layer = &list->hwLayers[i];
IMG_native_handle_t *handle = (IMG_native_handle_t *)layer->handle;
if (dsscomp->num_ovls < num.max_hw_overlays &&
can_dss_render_layer(hwc_dev, layer) &&
(!hwc_dev->force_sgx 11 is_protected(layer) 11
(hwc_dev->ext.current.docking
&& h w c _ d e v - > e x t . c u r r e n t . e n a b l e d && dockable(layer))) &&
m e m _ u s e d + memld(handle) < M A X _ T I L E R _ S L O T &&
! (is_BLENDED(layer) && fb_z >= 0 ) ) {
m e m _ u s e d += memld(handle); II准备使用DSS的特加层渲染
l a y e r - > c o m p o s i 巨 o n T y p e = HWC_OVERLAY;
if (hwc_dev->use_sgx && !is_BLENDED(layer))
layer->hints I= HWC_HINT_CLEAR_FB; II处理SGX渲染的情况
else if (is_BLENDED(layer) && i > 0)
h w c _ d e v - > o v l s _ b l e n d i n g = l;
hwc_dev->buffers(dsscomp->num_ovls] = layer->handle;
omap4_hwc_setup_layer(hwc_dev, II对层的硬件进行操作,包括像素尺寸处理
&dsscomp->ovls[dsscomp->num_ovls], layer, z,
handle->iFormat, handle->iWidth, handle->iHeight);
d s s c o m p - > o v l s ( d s s c o m p - > n u m _ o v l s ] . c f g . i x = dsscomp->num_ovls;
dsscomp->ovls(dsscomp->num_ovls) .addressing
= O M A P DSS B U F A D D R L A Y E R IX;
dsscomp->ovls[dsscomp->num_ovls] .ba = dsscomp->num_ovls;
II 省略部分内容
dsscomp->num_ovls++;
z++; // z的顺序进行增加
} else if (hwc_dev->use_sgx) { //继续处理使用SGX的情况
if (fb z < OJ {
fb z = z; z++;
} else {
w h i l e (fb z < z - 1) //将FrameBuffer的z顺序调到上面
dsscomp->ovls[l + fb_z++] .cfg.zorder--;


II 省略: GFX缩放的情况
if (scaled_gfx) d s s c o m p - > o v l s [ O ) . c f g . i x = dsscomp->num_ovls;
if (hwc_dev->use_sgx) (
I* assign a z-layer for fb *I
if (fb z < 0) { fb z = z++; ) //调整FrameBuffer的z顺序
h w c d e v 今 b u ffe rs [O ] = NULL;
o m a p 4 _ h w c _ s e t u p _ l a y e r _ b a s e (&dsscomp->ovls [0] .cfg, fb_z, I I调用设控层
沁 c _ d e v 今 fb_ d ev - >b a se .fo rm at , 1,
hwc_dev->fb_dev->base.width, hwc_dev->fb_dev->base.height);
dsscomp->ovls[OJ .cfg.pre_mult_alpha = 1;
dssco_!ll_p->ovls [_Q) •address ing = OMAP_DSS_BUFADDR_LAYER_IX;

�354
第21章 Android 4.x的音频、视频系统 ooe

d s s c o m p - > o v l s [OJ . b a = 0 ;

hwc 一 d e v - > p o s 七 2 l a y e r s = dsscomp->num_ovls; //对层的镜像


II 省 略 :扩 展 层 的处 理
for (i = z =ix = 0 ; i - < dsscomp->num_ovls; i + + ) { //处理各层的z顺序
st工uct dss2_ovl_cfg * c = &dsscomp->ovls[i] . c f g ;
z I = 1 << c - > z o r d e r ; / / c->zo过er用位的方式表示层的位置
i x I = 1 << c - > i x ;

d s s c o m p 今 mo d e = DSSCOMP SETUP DISPLAY; II设置模式


dsscomp->mgrs[OJ . i x = 0 ;
dsscomp->mgrs[OJ.alpha_blending = l ;
dsscomp->mgrs[OJ . s w a p _ r b = hwc_dev->swap_rb;
dsscomp->num mgrs = l ;
II 省略部分内容
p 七 h r e a d m u t e x u n l o c k (&hwc d e v - > l o c k ) ; I /线程的互斥解锁
r e t u r n 0;

o m a p 4 _ h w c _ p r e p a r e ( ) 函数是 H W C 硬件抽象层处理 的重要逻辑 ,包括 了各种参数计算 ,


层顺序 ( Z ) 的处理 ,使用 SG X 的处理等 内容 。此 函数执行 的过程基本上只是预设 ,没有
直接的硬件操作。
用于设置的 o m ap 4 _ h w c _ s e t ( ) 调用 了 o m ap 4 _ h w c _ r e s e t _ s c r e e n ( ) , 这个函数的实现主体
如下所示:
s t a t i c v o i d omap4_hwc_reset_screen(omap4_hwc_device_t *hwc_dev){
static int first_set = l;
int ret;
辽{丘rs七_set) (
f i r s t _ s e t = O;
s t r u c t d s s c o m p _ s e 七 u p _ d i s p c _ d a t a d = { .num_mgrs = 1, } ;
r e t = i o c 七 l ( h w c _ d e v - > d s s c o m p _ f d , DSSCIOC_SETUP_DISPC, & d ) ; I I 控 制 设 置 显 示
r e t = i o c t l ( h w c _ d e v - > f b _ f d , FBIOBLANK, F B _ B L A N K _ P O W E R D O W N ) ; I I 对 f b 的 操 作
r e 七 = i o c t l ( h w c _ d e v - > f b _ f d , FBIOBLANK, FB_BLANK_UNBLANK);

用于设置的 o m ap 4 _ h w c_ set ( ) 函数 的主要 内容如下所示 :

static int omap4_hwc_set(struct hwc_composer_device *dev, h w c _ d i s p l a y _ t dpy,


hwc_surface_t sur, hwc_layer_lis七_t* l i s t ) (
omap4_hwc_device_t *hwc_dev = (omap4_hwc_device_t * ) d e v ;
s t r u c t d s s c o m p _ s e t u p _ d i s p c _ d a t a *dsscomp = &hwc_dev->dsscomp_data;
i n t e r r = 0;
in七invalidate;
pthread_mu七ex_lock(&hwc_dev今 l oc k ) ; / /锁住互斥拯
omap4_hwc_reset_screen(hwc_dev); // 调用重设屏甜
i n v a l i d a t e = h w c _ d e v - > e x t _ o v l s _ w a n t e d && ! h w c _ d e v - > e x t _ o v l s ;
i f (debug) d u m p — s e t _ i n f o ( h w c _ d e v , lis七);

if ( d p y && s u r ) { / /只有显示层存在时,才会进行操作,否则相当于关闭了屏幕
江 ( h w c _ d e v 今 u s e_ s g x ) { I I如果使用SGX
if (! e g l S w a p B u f f e r s ((EGLDisplay) dpy, (EGLSurface) s u r ) ) { / I 交 换 图 层 显 示
e r r = HWC_EGL_ERROR; go七o e r r _ o u t ;

w r i t e (hwc_dev->pipe_fds (1), "s", 1);

355
•oo Android 板级支持与硬件相关子系统

i f ( h w c _ d e v 今 f o r c e_ s g x > 0) h w c _ d e v - > f o r c e _ s g x - - ;
e r r = hwc_dev->fb_dev->Post2((framebuffe工_device_t *)hwc_dev->fb_dev,
hwc_dev->buffers, hwc_dev->post2_1ayers,
dsscomp, s i z e o f ( * d s s c o m p ) } ; / / 调 用 釭 a m b u f f e r 设备

if (!hwc_dev->use_sgx} { / / 不 使 用 SGX: 对 釭 a m e B u f f e r 操作
u32 c r t = 0 ;
i n t e r r 2 = i o c t l ( h w c _ d e v - > f b _ f d , FBIO_WAITFORVSYNC, & c r t ) ; //等待同步
i f ( e r r 2 ) { er工 = e工工 ? : - e r r n o ; }

hwc d e v - > l a s t e x t o v l s = hwc d e v - > e x t o v l s ;


hwc d e v - > l a s t i n t o v l s = hwc d e v - > p o s t 2 l a y e r s ;
err_out:
p t h r e a d mutex unlock(&hwc d e v - > l o c k ) ; //解除互斥狱
i f ( i n v a l i d a t e && h w c _ d e v - > p r o c s && h w c _ d e v - > p r o c s - > 1 . n v a l i d a t e )
hwc_dev->procs->invalidate(hwc_dev->procs);
return err;

其中对 F r am eB u f f er 设 备进 行 了辅助控 制 操 作 ,是通 过 G r al l o c 模块 的


f r a m e b u f f e r _d e v i c e _t 完成 的 ,但 是所调用 的 P o st 议)
函数却是 OM 凡>4 系统特有 的硬件 。还
有多处关于是否使用 SG X 的判 断 ,原则是 当使用 SG X 时 ,才去调用 O p en G L 的标准接 口。
而 d ssc o m p 设置则提供 了到硬件设备 的控制功能 。

一提示:由于多层显示的特殊性, OM 凡 江的视频组合的硬件抽 象层 实现 已经与其他硬


件相关的子系统有所耦合。

�356
第22章
Android 4.x近场通信系统

22.1 近场通信系统概述

近场通信 (N FC , Near Field Communication) 也称为近距离无线通信 ,是 种短距 离
的高频无线通信技术,允许电子设备之间进行非接触式点对点数据传输,在 10cm 内交换
数据。近场通信与蓝牙、红外等系统有相似之处。
飞利浦和索尼共同研制开发了 N FC , 硬件上使用小型的单独芯片提供支持。 N FC 特 点
是成本低,结构简单,通信距离短。 N FC 可用于接触通过 、接触支付 、接触连接 (两个设
备的数据传输)等业务。
N F C 目前 成为 ISO /IE C IS 18092 国际标准 、E M C A -340 标准与 E T SITS 102 190 标准 。
NCI 是 N FC Controller Interface 的简称 ,N FC 控制器接 口规范 。N D E F 的含义则是 N FC 数
据交换格式 (N FC Data Exchange Format) 。N FC Fri (Forum Reference Implementation) 表
示 N FC 论坛对 N FC 标准 的参考实现 。恩智浦半导体 (N 双 > , 飞利浦的半导体部)提供了
N F C 芯 片的硬件支待也就是 PN 544 。
Android 系统在 A ndroid 2.3 版本开始 引入 N FC 的部分功能 ,在 A ndroid 4.2 版本 中,
N F C 的结构 已经 比较成熟 ,并使用 标准 的硬件模块作为硬件抽象层 。
Android 近场通信 的相关 内容如表 22-1 所示 。
表22-1 近场通信系统的相关内容
Android的层次 近场通信系统部分 描 述

硬件层次 UART、l2C等连接的特殊芯片 例如NXP的PN544

驱动程序层 简单的驱动程序 驱动程序可以控制连接即可

硬件抽象层 NFC硬件库 标准的硬件模块

本地层 libnfc和hbofc-nc1等库 可以直接被上层调用的本地库


Java层 框架层的android.nfc、com.android.nfc包 NFC包实现了框架层的接口

近场通信的前身是射频识别 (R FID , Radio Frequency IDentification) 技术 ,又称 电子


• O O Android板级支持与硬件相关子系统

标签、无线射频识别。完整 的RFID系统由三个部分所 组成:阅读器Reader、电 子标签Tag


(也就是应答器Transponder)和应用软件系统。其工作原理是: Reader发射 一 种特 定频率
的无线电波能量给Transponder, 用以驱动Transponder电路将内部的数据送出,此时Reader
便依序接收解读数据,送给应用程序做相应的处理。
近场通信采用主动和被动两种读取模式。
o 卡 模 式 ( C a r d emulation): 刷卡的场景,公交卡、门禁等。
o 点 对 点 模 式 ( P 2 P m o d e ) : 可用于数据交换,类似红外。
o读卡器模式(Reader/writer mode): 非接触的读卡器。

)提示:相比蓝牙系统,NFC的结构简单,但传输距离较短,速度较慢。

22.2 近场通信子系统的结构

�22.2.1 总体结构
Android近场通信系统自下而上包含了驱动程序、硬件抽象层、Java层等几个部分,如
图22-1所示。

Java应用层

Java框架层


亡勹 :扂言言呈言昙 芦
图22-1 Android的NFC系统结构

Cl)驱动层: NFC的驱动程序。
(2)硬件抽象层: NFC硬件模块。
e hardware/libhardware—legacy/include/hardware/nfc.h。
NFC系统是Android中标准 的硬件模块,实现后将生 成名称为nfc. <platform> .so的动
态库,放置在目标文件系统/system/lib/hw/目录中。
(3) NFC本地库。
NFC本地库在以下两个目录 中。
e extemal/libnfc-nxp/: N 双 )库 ,生成 libnfc_ ndef.so和libnfc.so。

358
第22章 Android 4.x 近场通信系统 ooe

e extemal/libnfc-nci/: N F C 控制器接 口规范的库 ,生成 lib n fc _ nci.so 。

(4) N F C 的 Ja v a 框架层 内容 。
e frameworks/base/core/java/android/nfc/: Java 框架层 的 内容 为 a n d ro id .n fc 包 。
e frameworks/base/core/jni/android_ nfc.h: 一个用于 JN I 的头文件 。
(5) Nfc 包。
packages/ a p p s / N fc/: 生成应用程序包 N fc .ap k , 其中包括了本地调用的 JN I 部分 ,负责

NFC 的主要逻辑 。

汕 22.2.2 NFC 本 地 库
libnfc 是 N 双汁是供 的 N FC 的支持库 ,其 中的源代码文件 比较 多。其 中还使用 了 目标 系
统的 /sy stem / v en d o r/fmn w are/ 目录 中的 lib p n 5 4 4 _ f w.so 固件库 。
L i n u x _ x 8 6 / p h D a l 4 N fc. c 文件 的 p h D a l4 N fc_ Config() 函数 的片段如下所示 :

N F C S T A T U S p h D a l 4 N f c _ C o n f i g ( p p h D a l 4 N f c _ s C o n f i g _ t c o n f i g , v o i d **phwref) {
N F C S T A T U S retstatus = NFCSTATUS_SUCCESS;
const hw_module_七* hw_module; // NFC的硬件模块
nfc_pn544_device_t* pn544_dev; // N X P 的 N F C 的 设 备
uintS_t num_eeprom_settings;
uintS_t* eeprom_settings;
int ret;
ret = hw_get_module(NFC_HARDWARE_MODULE_ID, &hw_module); //获得NFC硬件抽象层
if (ret) ( r e t u r n NFCSTATUS_FAILED; }
ret = nfc_pn544_open(hw_module, &pn544_dev); //得到nfc_pn544_device_t设备
if (ret) { return N F C S T A T U S _ F A I L E D ; )
c o n f i g - > d e v i c e N o d e = pn544_dev->device_node;
if (ret) { return N F C S T A T U S _ F A I L E D ; )
if ((config == NULL) 11 (phwref == NULL)) return NFCSTATUS_INVALID_PARAMETER;
memset(&gLinkFunc, 0, sizeof(phDal4Nfc link c b k interface t)); //初始化回调
switch(pn544_dev->linktype) //判断设备中的连接类型

case PN544 LINK TYPE UART: //对于UART和USB连接类型,采用相同的处理
case PN544 LINK TYPE USB:

gLinkFunc. init = p h D a l 4 N f c uart initialize;


gLinkFunc. o p e n _ f r o m _h a n d l e = p h D a l 4 N f c _ u a r t _ s e t _ o p e n _ f r o m_handle;
gLinkFunc.is_opened = phDal4Nfc_uart_is_opened;
gLinkFunc.flush = p h D a l 4 N f c uart flush;
gLinkFunc.close = p h D a l 4 N f c uart close;
g L i n k F u n c . o p e n _ a n d _ c o n f i g u r e = phDal4Nfc_uart_open_and_configure;
gLinkFunc. read = p h D a l 4 N f c uart read;
gLinkFunc.write = p h D a l 4 N f c uart write;
gLinkFunc. reset = p h D a l 4 N f c uar七reset;

break;
case PN544 L I N K T Y P E I2C: / I I2C连接类型的处理

gLinkFunc. ini t = p h D a l 4 N f c i2c initialize;
gLinkFunc. open_from_handle = phDal4Nfc_i2c_se七_open_from_handle;
gLinkFunc.is_opened = phDal4Nfc_i2c_is_opened;
gLinkFunc. flush = p h D a l 4 N f c i2c flush;
gLinkFunc.close = phDal4Nfc_i2c_close;
g L i n k F u n c . o p e n _ a n d _ c o n f i g u r e = phDal4Nfc_i2c_open_and_configure;
gLinkFunc. read = p h D a l 4 N f c i2c read;

359�
•oo Android板级支持与硬件相关子系统

gLinkFunc.write = phDal4Nfc_i2c_write;
gLinkFunc.reset = phDal4Nfc_i2c_reset;
break;

// 省略默认的处理

gLinkFunc.init(}; II回调函数初始化,也是连接端口的初始化
retstatus = gLinkFunc.open_and_configure(config, phwref);
if (retstatus != NFCSTATUS_SUCCESS) return retstatus;
(void)memset(&gDalContext,O,sizeof(phDal4Nfc_scontext_t));
pgDalContext = &gDalCon七ext;
II省略:初始化gReadWriteContext, gDalContext, 并将pn544_dev设置到gDalContex七
return NFCSTATUS_SUCCESS;

p h D a l 4 N f c_ C o n f i g ( ) 是 全 局 初 始 化 和 配 置 的 函 数 , 它 负 责 打 开 N F C 的 硬 件 抽 象 层 , 并
进行全局配置。
phLibNfc.c, p h H a l 4 N f c . c 等 文 件 提 供 了 N F C 的 核 心 实 现 。
src/p加bNfc.h文件提供了NFC的对外接口,内容如下所示:
......'一
typedef ui吐32_t phLibNfc_Handle; //表不NFC库的调用句柄
typedef void (*pphLibNfc_Connec七Callback_t} (void* pContext,
p,hLibNfc_Handle hRemoteDev,
phLibNfc sRemo迳Devinformation_t* psRemoteDevinfo, NFCSTATUS Status};
NFCSTATUS phLibNfc_M示_Ini豆alize (void * pDriverHandle,
pphLibNfc_RspCb一上 pinitCb, void* pContext};
NFCSTATUS phLibNfc_Mgt_Deinitialize(void * pDriverHandle,
pphLibNfc_RspCb_七pDeinitCb, void* pContext};
NFCSTATUS phLibNfc_Remo七eDev_NtfRegis七er(
phLibNfc_Registry_Info_t* pRegistryinfo,
phLibNfc_NtfRegister_RspCb_t pNotificationHandler,
void* p_Context'·

p h L i b N f c_ H a n d l e 类 型 是 N F C 库 的 调 用 句 柄 , 以 p h L i b N f c _ 开 头 的 函 数 用 于 客 户 端 和
NFC的库进行调用。

�22.2.3 Android框架层的NFC相关内容
NFC相关的框架层内容在android.nfc包中,主要包括API和AIDL接口。

1. 作 为 API 的 几 个 类
android.nfc中几个作为API的类如下所示。
• NfcManager: N F C 部 分 应 用 程 序 调 用 的 入 口 。
• NfcAdapter: 表 示 一 个 N F C 设 备 (适 配 器 ),包 括 使 能 禁 止 、标 签 读 写 、N D E F 数 据
交换,以及点对点的功能。

• NdefMessage: 表 示 N D E F 的 消 息 , 包 括 个 或 多 个 N d ef Reco rd s 。

• N defRecord: 表 示 N D E F 中 的 个单元 。
• NfcEvent: 表 示 各 种 N F C 数 据 。
• Tag: 表 示 N F C 被 发 现 的 T A G 。
与其他硬件相关的内容类似,以"nfc"为参数调用Context类的getSystemService()方法

�360
第 22 章 Android 4.x 近场通信系统 ooe

可以得到 N fcM anager, 从 N fcM anager 又可 以得到 N FC 的主要控制类 N fcA dapter。


NfcAdapter 类 中的主要方法如下所示 :
p u b l i c static N f c A d a p t e r g e t D e f a u l t A d a p t e r (Con七ext context)
p u b l i c v o i d e n a b l e F o r e g r o u n d D i s p a t c h (Activity activity,
Pendingintent intent, IntentFilter[] filters, String[][]七echLists)
p u b l i c v o i d setNdefPushMessage (NdefMessage message,
A c t i v i t y activity, A c t i v i t y ... activities)
public void setNdefPushMessageCallback (
NfcAdap七er.CreateNdefMessageCallback callback,

对于 一 个 Tag 发现 ,一 般可 以通过分发消息得到 ,调用 enableForegroundDispatc 风) 使


能分发之后,系统将分发 Intent 给前 台的 A ctivity 。setN detPushMessage() 两个方法用 于点对
点 的 C P 2 P ) 处 理 , 此 时 的 N FC 设备使用 的是 N D EF 协议。

2. 几 个 A I D L 接 口
android.nfc 包 中另有几个 A ID L 文件定义远程 的接 口,用于 N FC 实际功能的实现 ,包
括 IN fcAdapter 、IN fcTag 和 IN fcA dapterExtras 等 。
INfcAdapter.aidl 文件 中 IN fcA dapter 接 口的 内容如下所示 :
interface INfcAdapter

INfcTag getNfcTaginterface();
INfcAdapterExtras g e t N f c A d a p t e r E x t r a s i n t e r f a c e ( i n S t r i n g pkg);
int g e t S t a t e ();
b o o l e a n d i s a b l e ( b o o l e a n saveState);
boolean enable();
b o o l e a n enableNdefPush();
b o o l e a n disableNdefPush();
b o o l e a n isNdefPushEnabled();
v o i d s e t F o r e g r o u n d D i s p a t c h ( i n Pendingintent intent,
in IntentFilter[J filters, in TechListParcel techLists);
v o i d setNdefPushCallback(in I N d e f P u s h C a l l b a c k callback);
v o i d d i s p a t c h ( i n Tag七ag);
v o i d setP2pModes(int ini七atorModes, in七targetModes);

除了几个设置接口之外, setForegroundDispatchO 和 setN defPush Callback() 是 N FC 主要


的实现部分,它们和 A PI 相对应 。N FC 程序运行 的基本逻辑是 :首先 由 N FC 的实现部分
检测标签,然后向上层发送 Intent, 应用得到数据 (N defRecord ) 后进行处理 。

迪 22.2.4 NFC 包

N F C 包是 一 个带有 JN I 的 A ndroid 应用程序包 ,包名为 com .android.nfc, 这个包运行


于 A ndroid 特殊 的 N FC 功能 ,实际上它是 N FC 服务 的实现者 ,提供 了 N FC 框架层 的功能 。
之所以使用 一 个应用程序包实 现 N FC 服务 ,主要是 因为该服务涉及 了较多的上层细节 ,不
便在框架层的 System Server 进行这些细节 的处理 。

1. 核心部分
NfcService 类则是 N FC 的核心 ,它实现 了 A ID L 定义的远程接 口。

361�
•oo Android板级支持与硬件相关子系统

N f c S e r v i c e 中 包 括 几 个 嵌 套 类 , N f c A d a p t e r S e r v i c e 类 继 承 了 I N fcAdapter. Stub, 通 过
S e r v i c e M a n a g e r 注 册 为 A n d r o i d 的 名 称 为 " n f c " 的 服 务 ; T a g S e r v i c e 类 继 承 了 I NfcTag. Stub;
N f c A d a p t e r E x t r a s S e r v i c e 继 承 了 I NfcAdapterExtras.Stub。InfcAdapter、INfcTag和
I Nf c A d a p t e r E x t r a s 等 远 程 接 口 , 都 是 在 框 架 层 的 a n d r o i d . n f c 包 中 定 义 的 。
另 一 个 重 要 的 类 是 N fc S erv iceH an d ler, 继 承 了 H a n d l e r 类 , 用 于 事 件 的 处 理 。 这 个 类
的重要片段如下所示:
丘nal class NfcServiceHandler ex七ends Handler {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
// 省略:各种其他消息的处理
case MSG NDEF TAG: //发现新的TAG
TagEndpoint t a g = (TagEndpoint) msg.obj;
playSound(SOUND_START);
NdefMessage ndefMsg = tag.findAndReadNdef(); / / 得 到 新 TAG 的消息
if (ndefMsg != null) {
tag.startPresenceChecking(); //开始检查TAG
dispatchTagEndpoint(tag);
) else {
江 ( t a g . reconnect ()) { / / 判 断 TAG 重新连接的结果
tag.star七PresenceChecking(); //开始并进行分发
dispatchTagEndpoint(tag);
) else {
tag.disconnect();
playSound (SOUND ERROR); //停止并播放声音

break;
case MSG CARD EMULATION: //卡的枚举事件
byte[] a i d = (byte[]) msg.obj;
Intent aidintent = new Intent () ; . / /准备广播的数据
aidintent.setAction(ACTION AID SELECTED);
aidintent.putExtra(EXTRA_AID, aid);
sendSeBroadcast(aidintent); //发送广播
break;
II 省略:各种其他消息的处理

以MSG_为开头的各种消息就是进行交互的手段。sendSeBroadcast()也是
NfcServiceHandler中的方法,用于对安装包来发送广播,起到通知的作用。

2. 设 备 相 关 的 部 分
NXP和NCI两个目录都包括了NXP的NFC和NFC控制器接口规范的内容,均包括
Java和本地代码。Java包为com.android.nfc.dhimpl, 表 示 N F C 硬 件 设 备 层 的 实 现 内 容 , 各
个Java类均以Native为开头,实际上就是对本地函数的封装。两个目录的本地库分别是
libnfcjni.so和libnfc—ncijni.so, 它 们 直 接 调 用 了 N F C 底 层 的 库 。
N戏和NCI两个包的结构类似,Nati v e Nf c M a n a g e r 是 其 中 核 心 的 实 现 类 ,
NativeLlcpSocket和NativeLlcpServiceSocket两个类用于提供套接字的操作。LLCP的含义

�362
第22章 Android 4.x 近场通信 系统 ooe

是逻辑链路控制协议 ( L o g i c al L i n k C o n t r o l P r o t o c o l ) 。
nxp/jni/ 目录 中的 c o m android nfc NativeNfcManager.cpp 文件是 N FC 的主要实现 ,其
中的 n f c j n i _ i n i t i a l i z e ( ) 函数使用 了 N F C 的硬件抽象层 ,如下所式 :
ret = hw_get_module(NFC_HARDWARE_MODULE_ID, & h w _ m o d u l e ) ; / / 打 开 硬 件 模 块
if ( r e t } { go七o c l e a n _ a n d _ r e t u r n ; )
r e t = n f c _ p n 5 4 4 _ o p e n (hw_module, &pn544_dev}; / / 得 到 n f c _ p n 5 4 4_ d ev i c e_ t 设 备
i f ___ t r e t ) { g o t . 9 c l e a n _ a n d _ r e t u 互 p ; )

n f c j n i _ c o n f i g u r e _ d r i v e r ( ) 函数 的内容如下所示 :

static int nfc jni configure driver(struct nfc_jni_native_data *nat){


c h a r v a l u e [ P R O P E R T Y VALUE M A X ] ;
i n t r e s u l 七 = FALSE;
NFCSTATUS s t a t u s ;
// 省略错误处理
g D r v C f g . n C l i e n t i d = phDal4Nfc_msgget(O, 0600); //得到消息
REENTRANCE_LOCK();
s t a t u s = phLibNfc_Mgt_ConfigureDriver(&gDrvCfg, &gHWRef); //配置驱动
REENTRANCE UNLOCK(};
// 省略错误处理
if(pthread_create(&(nat->thread), NULL, nfc_jni_client_thread, nat) ! = 0) [
goto clean_and_return;

driverConfigured = TRUE; //设置驱动配置的标志


c l e a n and r e t u r n :
return result;

p h D a l 4 N f c _ m s g g e t ( ) 等 函数都是 l i b n f c 库 中的 内容 ,也创 建 了用千处理 N FC 调用 的线


程 n f cj n i _ c l i e n t _ t h r e a d 。

n f c j n i _ i n i t i a l i z e ( ) 函数后面与 N F C 硬件相关 的 内容如下所示 :


for ( i = 0 ; i < pn544_dev->num_eeprom_settin,gs; i + + ) {
c h a r e e p r o m _ p r o p e r t y [PROPERTY_KEY_MAX]; I I EEPROM 中使用 的键和键值
c h a r eeprom_value[PROPERTY_VALUE_MAX];
u i n t 8 _ t * eeprom_base = & ( p n 5 4 4 _ d e v - > e e p r o m _ s e t t i n g s [ i * 4 ] ) ;
I I C h e c k f o r o v e r r i d e o f t h i s EEPROM v a l u e i n p r o p e r t i e s
snprintf(eeprom_property, sizeof(eeprom_property),
" d e b u g . n f c . e e p r o m . 沧02X皂02X", e e p r o m _ b a s e [ l ] , e e p r o m b a s e [ 2 J ) ;
if ( p r o p e r t y _ g e t ( e e p r o m _ p r o p e r t y , e e p r o m _ v a l u e , " " ) = = 2) ( I I 从属性 中得 到值
i n t eeprom_value_num = ( i n t ) s t r 七 o l ( e e p r o m v a l u e , (char**)NULL, 1 6 ) ;
eeprom_base[3] = eeprom_value_num; I I 设置 EEPROM 中的值

g i n p u t P a r a m . b u f f e r = eeprom_base; //赋值到全局的变挹中
g i n p u t P a r a m . l e n g t h = Ox04;
gOutputParam.buffer = resp;
REENTR邸CE L O C K ( ) ;
s t a t u s = p h L i b N f c M g t I o C t l ( g H W R e f , NFC MEM WRITE, I I 调用 NFC 库 的接 口
& g i n p u t P a r a m , & g O u t p u t P a r a m , n f c _ j n i _ i o c t l _ c a l l b a c k , (vo.Ld * ) & c b _ d a t a ) ;
REENTRANCE UNLOCK();
// 省略错误处理

循环中的 一 个逻辑 是对 eep r o m _ s e t t i n g s 的 内容进行 处理 ,如 果调用 p r o p er t y—g et ( ) 从

Android 的属性系统 中得 到 了值 ,就可用它覆盖默认 的值 。此逻辑用来 为不 同系统配置 。

363�
• O o Android 板级支持与硬件相关子系统

对 T a g 的处理是此处需要处理 的逻辑 ,包括发现 、扫描和上传信息等部分 。

- -—”
N f c M a n a g e r 类 中 的 en ab l e D i sc o v er y ( ) 方 法将 使 能发现 流 程 ,其 中调 用 的本地 函数在

c o m android nfc NativeNfcManager.cpp 中如下所示:


-"-
s t a t i c · v o i d com a n d r o i d n f c N f c M a n a g e r e n a b l e D i s c o v e r y { J N I E n v * e , j o b j e c t o ) {
NFCSTATUS r e t ;
struct nfc jni native_data *nat;
CONCURRENCY L O C K { ) ;
n a t = n f c j n i g e 七 n a t {e, o ) ;
REENTRANCE L O C K { ) ;
r e t = p h L i b N f c _ R e m o t e D e v _ N t f R e g i s t e r { & n a t - > r e g i s t r y _ i n f o , //注册回调,等待通知
n f c j n i Discovery_notification_callback, {void *) n a t ) ;
REENTRANCE UNLOCK{);
if(ret ! = NFCSTATUS_SUCCESS) { g o t o c l e a n _ a n d _ r e t u r n ; )
// 省略:打印 n a t 今 r e g i s t r y _ i n f o 中的各个成 员
nfc_jni一江art_discovery_locked{nat, false);
c l e a n and r e t u r n :
CONCURRENCY_UNLOCK{);

p h L i b N f c — R e m o t e D e v_ N t f R e g i s t e 式)
用于传入 回调 函数 ,等待远程 设备 的通 知 ,比较特
殊的地方是每次都需要重新注册。
N ativeNfcManager 中以 notif y 开 头 的各 个 方 法 用 于 从 JN I 层 得 到 通 知 。例 如 ,
n o t i f y L l c p L 叫 扒 c t i v a t i o 顶)
方法用 于 点对 点 (P2P ) 的连接 ,n o t i f y N d e f M e s s a g e L i s t e n e r s ( ) 方
法用于发现新的 TA G 之后 ,它们就在本地 的 n f cj n i _ D i s c o v e r y _ n o t i f i c a t i o n _c a l l b a c k ( ) 函数

中完成调用,片段如下所示:
i f ( ( r e m D e v i n f o - > R e m D e v T y p e == p h N f c _ e N f c I P l _ I n i t i a t o r )
I I ( r e m D e v i n f o - > R e m D e v T y p e == p h N f c _ e N f c I P l _ T a r g e t ) ) {
h L l c p H a n d l e = remDevHandle; / / 保 存 P2 P 设备 的句柄
e->CallVoidMethod(nat->manager, //通知 J av a 层 的 Nf c Ma n a g e r
cached_NfcManager_notifyLlcpLinkActivation, t a g ) ;
II 省略错误处理
) else {
e 今 Ca l l Vo i d.M e t h od (n a t - >ma n ag e r , I I 通 知 J av a 层 的 Nf c Ma n a g e r
cached_NfcManager_notifyNdefMessageListeners, t a g ) ;
// 省略错误处理

两种通知属于对不同场景的处理,分别是连接到点对点设备和对 T ag 的处理 。

在处理中,当有 个新的 T ag 被 发 现 时 ,N f c S e r v i c e . Nf c S e r v i c e H a n d l e r 类将 处理
MSG_NDEF_TAG 消息 ,也就是调用 了 N at i v eN f c T ag 类 中的 r e ad N d ef O 方法 ,进而调用 J N I 。
c o m _ a n d r o i d _n f c_ N a t i v e N f c T a g . c p p 中的函数如下所示 :
s t a t i c j b y t e A r r a y c o m _ a n d r o i d _ n f c _ N a t i v e N f c T a g _ d o R e a d ( J N I E n v * e , j o b j e c t o) {
NFCSTATUS s t a t u s ;
p h L i b N f c _ H a n d l e h a n d l e = O;
j b y t e A r r a y b u f = NULL;
s t r u c t n f c , 'j "n i c a l l b a c k _ d a t a c b _ d a t a ;
CONCURRENCY L O C K ( ) ;
i f ( ! n f c _ c b _ d a t a _ i n i t ( & c b _ d a t a , NULL)) { g o t o c l e a n _ a n d _ r e t u r n ; }
handle = nfc_jni_get_connected_handle(e, o ) ;
n t c j_n;i. n4�f r w , J e n g t h = nfc_jni_nq旦f_buf_len; / /_内存的地址和长度

�364
第22章 A n d r o i d 4.x 近场通信 系统 ooe

n f c jni ndef iw.buffer = nfc_jni_ndef_buf;


R E E N T R 邸 C E LOCK();
s t a t u s = p h 区 b N f c _ N d e f _ R e a d ( h a n d l e , &nfc_jni_ndef_rw, //调用NFC库中的函数
ph巨bNfc_Ndef_EBegin, nfc_jni_tag_rw_callback, (void *)&cb_data);
R E E N T R A N C E UNLOCK();
II 省略错误处理
b u f = e->NewBy七eArra.y(nfc_jni_ndef_rw.leng七h);
e->SetByteArrayRegion(buf, 0, nfc_jni_ndef_rw.length, II向Java层返回数据内容
(jbyte *) nfc_jni_ndef_rw.buffer);
c l e a n a n d return:
nfc cb data d e i n i t ( & c b data); II取消回调的内容
C O N C U R R E N C Y UNLOCK();
return buf;

其中的核心部分是 p h L ib N fc_ N d e f _ Read(), 调用了 lib n fc 本地库 中的 内容实现 了读取


信息,得到信息后再交由 Jav a 层进行处理 。

22.3 近场通信BSP的结构

NFC 的B SP 由驱动程序和硬件成像层组成 。
NFC 硬件成像层有两种接 口:一 种是 N C I 接 口,另

种是 N 双 顷 勺N F C 特 定接 口。它
们共用 n fc .h 头文件 ,各 自有动态库 的实现两个模块 的 ID 分别为"n fc_ n c i" 和 "n fc " 。如果使
用 N 戏 的N FC 芯片 ,应 当使用后者 ;而所有基于 N C I 接 口的控制器应该使用前者 。

�22.3.1 NFC-NCI接口
NCI ( N F C Controller Interface) 表示 N FC 控制器 的通用接 口。
回调函数和硬件模块设备的定义如下所示:
typedef v o i d (nfc_stack_callback_七) (nfc_event_t event, nfc_sta七us_t event_status);
typedef v o i d (nfc_stack_da七a_callback_七) (uin七16一七 data_len, uint8_t* p_data);
typedef struct nfc_nci_device {
struct h w _ d e v i c e _ t common;
int (*open) (cons七struct nfc nci device *p_dev, //打开设备
nfc_stack_callback_飞; *p_cback, nfc_s七ack_data_callback_t *p_data_cback);
int (*write) (const struct n f c _ n c i _ d e v i c e *p_dev, //写数据
u i n t 1 6 _ t data_len, const u i n t 8 _ t *p_da七a);
int (*core_initia扛zed) (cons七struct n f c _ n c i _ d e v i c e *p_dev, II NFC芯片初始化
uint8 t* p core 1.nit rsp params);
int (*pre_discover) (const struct nfc_nci_device *p_dev); II每个RF发现之前调用
int (*close) (const struct nfc nci device *p_dev); II关闭设备
int (*control_gran七ed) iconst struct n f c _ n c i _ d e v i c e *p_dev);
int (*power_cycle) (const s t r u c 七 n f c _ n c i _ d e v i c e *p_dev);
) n恁f: f\C士呈eyice一岁

nfc_nci_device_t 中的各个 函数基本上都是基于设备 的原始操作 。打开设备 时调用 o p en


并传入回调函数的指针, w rite 用于直接 向 N FC 芯片 中写输入 ,这个 函数可 以使用 队列 的
形式并立刻返回, co re_initialized 用千设备 的初始化 ,p re—山sco v er 在每个 R F 发现之前被调
用,可以用于不同设备的特定操作。 N FC 芯片 的消息则通过 回调函数传给调用者 。

365
•oo Android板级支持与硬件相关子系统

�22.3.2 NFC接口
PN544是恩智浦半导体(N双门推出的NFC芯片。该芯片有主控制器接口(Host Control
Interface)的概念,可以支持多种连接到主机的接口。
连接方式的类型定义如下所示:
PN544typedef e n u m {
PN544 L I N K T Y P E UART, // UART的连接
PN544 LINK TYPE I2C, // i2c的连接
PN544 LINK T Y P E USB, // USB的连接
PN544 LINK T Y P E INVALID, //无效
} nfc_pn544_link七ype;

NFC驱动程序和硬件的连接方式有关,使用串口、USB和I2C的标准驱动程序。
表示NFC设备的nfc_pn544 _device_t结构如下所示:
七ypedef struct {
struct h w d e v i c e t common;
u i n t 3 2 _ t num_eeprom_settings;
uint8_t* eeprom_se七tings; // format [OxOO, addr msb, addr_lsb, value]
n f c _ p n 5 4 4 _ l i n k t y p e linktype; // UART、i2c、USB等连接方式
const char* device_node;
uint8 t e n a b l e i2c workaround;
uint8 t i2c device address;
I nfc_pn5江device_t;

NFC本身可以使用UART、I2C、USB等连接方式,这也就是在nfc_pn544_device_t
设备的nfc_pn544_linktype类型的linktype所能设置的值。enable_i2c_workaround和
i2c_device_address是只有在12C的情况下才会被使用的。eeprom_settings表示了在EEPROM
中的设置,其中每个地址占4个字节,以OxO为开始,高地址、低地址和数值。
其中支待的nfc_pn544_device_t是 一 种类型的设备,这实际上就是 N 双边邓]的芯片。

22.4 近场通信BSP的实现

�22.4.1 NCI-NFC的桩实现
Android 4.2中基于NCI的NFC实现的代码路径为libhardware/modules/nfc-nci, 只有 一
个源代码文件nfc_ nci_example.c, 其中的内容将生成nfc_nci.default.so。
本NFC设备的名称为"Default N F C NCI HAL", 其中各个函数均为空实现。

汕22.4.2 NFC的桩实现
Android 4.2中NFC实现的代码路径为libhardware/modules/nfc, 只有 一 个源代码文件
nfc _pn544_example.c, 其中的内容将生成nfc.default.so。
本NFC设备的名称为"Default N F C H W HAL", 在openO函数中,设备类型为PN544
LINK_ TYPE_ INVALID, 因此这里的内容是 一 个空实现。

�366
第22章 A n d r o i d 4.x 近场通信系统 ooe

�22.4.3 Galaxy Nexus的NFC实现


OM战Tuna 板也就是 G alax y N e x u s 系统 的基础平 台,G alax y N e x u s 系统具有 N FC 硬
件,也就是使用 T una 板 。在硬件上使用 了 N X P 的 PN 54 4 芯片,使用 U A RT 作为连接方式 ,
使用标准的 TTY 设备 的驱动程序 。
硬件抽象层代码的路径为 d ev ice/sam su n g /tu n a/n fc , 其中的内容将生成 n fc .tun a .so 动态
库。 n fc —
hw .c 中的 n fc_ o p e 顶)
函数是设备 的入 口。核心 内容是 p n 5 4 4 _ eedata settin g s , — 也就
是 EEPR O M 中所需要使用 的命令 。主要 的代码 内容如下所示 :
sta七ic uin七8_t pn544_eedata_settings[) [4] = (
// R F Se七tings
{OxOO, Ox9B, OxDl, Ox OD) / / Tx consumption h i g h e r than Ox OD (average 50'mA)
, (OxOO, Ox9B, OxD2, Ox24} / / G S P setting for this t h r e s h o l d
II省略部分内容
);
static int n f c _ o p e n ( c o n s t hw_module_t* module, c o n s 七 c h a r * name,
h w device t** device) {
if (strcmp(name, NFC_PN544_CONTROLLER) = = 0) {
n f c _ p n 5 4 4 _ d e v i c e 七 * d e v = calloc(l, sizeof(nfc_pn544_device_t));
d e v - > c o m m o n . t a g = HARDWARE DEVICE TAG;
d e v 今 c om m o n .v e r sion = O;
d e v - > c o m m o n . m o d u l e = (struct hw_module_t*) module;
d e v - > c o m m o n . c l o s e = pn544_close;
d e v - > n u m e e p r o m settings = sizeof(pn544 eedata se七七ings) / 4;
d e v - > e e p r o m _ s e t 巨 n g s = (uint8一七*)pn544_eedata_settings;
dev-> l i n k t y p e = PN544 L I N K T Y P E UART; //串口方式
d e v - > d e v i c e _ n o d e = "/dev/tty03"; II串口的设备节点
d e v - > e n a b l e i2c w o r k a r o u n d = O;
*device = (hw device七*) dev;
return 0;
) else (
return -EINVAL;


p n 5 4 4 eed ata_ settings 就是在 E E P R O M 中的数据 ,在此处使用 U A RT 的连接方式 ,以
/dev/tty03 为 串口设备的节 点 。在这个 N F C 的硬件抽象层 中,只考虑 了 E E P R O M 的设置数
据和串口信息,其他内容交由上层软件完成。

367�
第23章
Android 4.2的电源控制

23.1 电源控制

电源控制是 A ndroid 4.2 增加 的一 个辅助功能的模块 ,可 以对 电源控制进行 一 部分干涉


和控制。目前电源控制部分主要用千在用户交互发生变化时,通过特殊的设置控制电源相
关的硬件,以达到节省能源的目的。
电源硬件模块不同于 A ndroid 用于 电源管理 的 w ake_lock 部分 。

23.2 电源控制的结构

迪23.2.1 总体结构
电源控制包括了硬件抽象层和 Surfaceflinger 中的调用部分。
e hardware/libhardware/include/hardware/power.h: 硬件抽象层的头文件。
电源控制的调用者是 Surfaceflinger。
e frameworks/native/services/surfaceflinger/DisplayHardware/PowerHAL又
实际上电源控制部分的所有接口并未充分使用,目前默认是在 Surfaceflinger 中使用 ,
用于在垂直同步的 V Sync 信 号产生时控制 C PU 的频率 。

�23.2.2 电源控制的使用
Surface flinger 中 的 Pow erH A L .cpp 调用 Pow er 部分 中的处理如下所示 :
s t a t u s _ t PowerHAL::vsyncHint(bool enabled) {
if (!mPowerModule) ( r e t u r n ,

NO I N I T ; }
i f ( m P o w e r M o d u l e - > c o m m o n . m o d u l e a p i v e r s i o n > = POWER MODULE A P I VERSION O 2 ) {
i f (mPowerModule->powerHint) {
if (mVSyncHintEnabled ! = b o o l ( e n a b l e d ) ) {
mPowerModule->powerHint(mPowerModule, //调用硬件抽象层
POWER" H I N T " VSYNC, ( v o i d * ) e n a b l e d ) ;
第23章 Android 4.2的电源控制 o o e

m V S y n c H i n t E n a b l e d = bool(enabled);

return NO_ERROR;

vsyncHint()函数的内容就是当VSync信号发生时,调用电源控制,参数可以是使能
(true)和禁止(false)。
Surfaceflinger表示事件循环EventThread.cpp文件中的enableVSyncLocked()和
disable VSyncLocked()函数分别以true和false为参数调用了vsyncHint()函数。使用在
EventThread中的waitForEvent()函数中,内容如下所示:
if (timestamp && !waitForVSync} {
disableVSyncLocked(}; //禁止VSYNC
l els e if (! 巨 m e s t a m p && waitForVSync} { //有客户端等待
enableVSyncLocked(}; //使能VSYNC

调用的逻辑,主要根据有没有客户端等待,选择使能和禁止Vsync。

23.3 电源控制BSP的结构

硬件抽象层的power.h文件中定义的接口内容如下所示:
'·typedef e n u m {
P O W E R HINT V S Y N C = OxOOOOOOOl,
P O W E R HIN T INTERACTION = Ox00000002,
J p o w e r hint t;
typedef s t r u c 七 p o w e r _ m o d u l e {
struct h w m o d u l e 七 c o m m o n ;
v o i d (*ini七) (struct power_rnodule *module);
v o i d (*setinteractive) (struct p o w e r _ m o d u l e *module, int on);
v o i d (*powerHint) (struct power_rnodule *module, power_hint一七 hint, v o i d *data);
}主尸。义t.,r蓝sJ, le贮

setlnteractive用于控制设置交互状态的开关。powerHint用于显示的部分调用电源的
控制方面。例如显示vsync (垂直同步信号)的提示是 一 种情况,而用户交互时又是另

种情况。

23.4 电源控制BSP的实现

请 23.4.1 通用的电源控制实现
Android系统包括了通用电源的实现,其代码路径为libhardware/modules/power, 只有

个源代码文件 pow er.c,其中的内容将生成power.default.so。此处的实现是 一 个空的实现。
init、setlnteractive和powerHint三个函数指针均为空,因此没有实际的电源操作。

369�
• O O Android板级支持与硬件相关子系统

迪23.4.2 Galaxy Nexus的电源控制实现


Galaxy Nexus使用OMAP处理器的电源控制方式,驱动程序提供sys文件系统的文件,
电源控制的硬件抽象层在用户空间写文件。

1. 驱动程序
O M A P 的 内 核 中 关 于 C P U 频 率 控 制 由 一 个单独 的驱动程序实现 ,驱动程序 的代码路径
为如vers/cpufreq/cpufreq_interactive.c, 使用sys文件系统作为用户空间的接口。其中用千
控制boostpulse的接口如下所示:
static ssize_t store_boostpulse (struct kobject *kobj, struct att-ribu七e *attr,
/ const char *buf, size_七count} {
int ret;
u n s i g n e d long val;
ret = ks七rtoul (buf, 0, &val) ; //获取参数的内容
if (ret < 0) return ret;
trace_cpufreq_interactive_boos七("pulse");
c p u f r e q _ i n t e r a c t i v e boost(); //实际执行功能
re七urn count;

static struct global_at七r b o o s t p u l s e =


_ATTR(boos七pulse, 0200, tiULL, storeJ巨oostpulse);

此处生成的文件就是/sys/devices/system/cpu/cpufreq/interacti ve/目录中的boostpulse, 此
文件是只写的(不可读),对其写表示控制CPU。cpufreq_interactive_ boostO函数中,将根
据表示CPU信息的结构体进行计算和设置。
同在sys文件系统的interactive目录中,还包括了儿个可读写的文件: timer_rate (表示
的定时器时间ms, 一 般 为 2 0 000), min_sample_time (最小的采样时间ms, 一 般 为 6 0 000),
hispeed_freq ( 最 高 频 率 M H z , 一般为700 000)等文件作为控制接口。

2. 硬件抽象层程序
Galaxy Nexus系统电源控制硬件抽象层代码的路径为device/samsung/tuna/power, 其中
的内容将生成power.tuna.so动态库。
power_tuna.c文件中使用sys文件系统的文件作为控制手段,相关的代码如下所示:
#define B O O S T P U L S E _ P A T H "/sys/devices/system/cpu/cpufreq/interactive/boostpulse"
static int b o o s t p u l s e _ o p e n ( s t r u c t tuna_power_module *tuna) {
char buf[BOJ;
pthread_mu七ex_lock(&tuna->lock);
if (tuna->boostpulse_fd < 0) {
七una->boostpulse fd = o p e n ( B O O S T P U L S E PATH, 0 WRONLY); //打开设备
II 省略部分内容

pthread_mutex_unlock(&tuna->lock);
return tuna 今 b o o stp u l se_ fd ;

boostpulse文件被打开之后,保存成了文件描述符,在tuna_power_hint()函数的实现中,
H IN T —
POWER — IN T E R A C T IO N 命令就通过写文件描述符完成 。

370
第23章 Android 4 . 2 的 电 源 控 制 ooe

实 现 s e t _ interactive和power—init的两个函数如下所示:

static void tuna_power_set_interactive(struct p o w e r _ m o d u l e *module, int on) {


sysfs_write("/sys/devices/system/cpu/cpuO/cpufreq/scaling_max—freq",
o n ? "1200000" : "700000");

s t a t i c v o i d t u n a _ p o w e r _ i n i t { s t r u c t p o w e r _ m o d u l e *module) (
sysfs_write("/sys/devices/system/cpu/cpufreq/interactive/timer_ra七e",
"20000");
sysfs_write{"/sys/devices/system/cpu/cpufreq/intera tive/min_sample_七ime",
"60000");
sysfs_write{"/sys/devices/system/cpu/cpufreq/interactive/hispeed_freq",
"700000");
II 省略部分内容

其中都通过写sys文件系统控制各种内容,控制CPU频率相关的内容。

371�
第24章
本地时间


本地时间是Android 4.2新增的 个 辅 助 功 能 的硬 件 抽 象 层 模 块 ,用 于 得 到 系 统 的 时 间
和频率。这些信息原本可以通过Linux标准的方式获取和设置,也可以由单独的硬件提供,
并封装成单独的硬件模块。本地时间目前主要被多媒体调用。

24.1 本地时间子系统结构

24.1.1 本地时间的结构

本地时间是 个 辅 助 的模 块 ,用 于 得 到 本 地 的 时 间 和 频 率 。 从 硬 件 的 角 度 ,用 于 获 取
时间的部分可能由单独的硬件提供,也可以对其进行获取和设置。
4t hardware/libhardware/include/hardware/local time hal.h: 硬件抽象层头文件。
框架层对本地时间调用封装成local_clock模块。

4t frameworks/av/include/common time/local clock.h: local clock 头文件。
4t frameworks av/media/common time/local cloc k.cpp: local cloc k源代码。
此 处 的 内 容 生 成 l i b c o m m o n_time_client.so动态库。

24.1.2 本地时间的使用
local_ c l o c k . c p p 实 现 了 L o c a lC l o c k 类 , 其 构 造 函 数 如 下 所 示 :
Mu七ex L o c a l C l o c k : : d e v l o c k ;
l o c a l _ t i m e _ h w _ d e v i c e _ t * L o c a l C l o c k : : d e v _ = NULL;
LocalClock::LocalClock() {
int res;
c o n s t hw m o d u l e t * mod;
Au七oMutex l o c k ( & d e v _ l o c k _ ) ;
if ( d e v _ ! = NULL) return;
r e s = h w _ g e t _ m o d u l e _ b y _ c l a s s ( L O C A L _ T I M E _ H A R D W A R E _ M O D U L E _ I D , NULL, & m o d ) ;
if (res) {
A L O G E ( " F a i l e d t o o p e n l o c a l t i m e HAL m o d u l e ( r e s = 号d ) " , r e s ) ;
) else {
r e s = l o c a l _ t i m 己 w _ d e v i c e _ o p e n ( m o d , & d e v _ ) ; I I 打 开硬住设备
第24章本地时间 ooe
......省略错误处理

Local Clock类中实现了几个时间相关的函数,如下所示:
int64_t LocalClock: :getLocalTime (} { //获取时间
assert (NULL != d e v_} ;
a s s e r t ( N U L L != dev_->get_local_time};
return dev_->get_local_time(dev_};

uint64一七 LocalClock::getLocalFreq() { //获取频率


a s s e r t ( N U L L != dev一);
a s s e r t ( N U L L != dev_->get_local_freq);
return dev_->ge七一local_freq(dev_);

status_t LocalClock::setLocalSlew(intl6一七 ra七e) { II设置转换频率
a s s e r t ( N U L L != dev一);
if (!dev_->set_local_slew)return INVALID_OPERATION;
return static_cast<status_t>(dev_->set_local_slew(dev_, rate));

以上几个函数都是调用local_time硬件抽象层的同名函数指针完成,硬件抽象层中的
函数指针可以为空,会被此处assert报错。
AudioFlinger使用了local_clock模块,调用了getLocalFreq ()获得本地的频率,处理与
采样频率相关的内容。

24.2 本地时间 B SP 的结构


Android 4.2使用local_time_hal.h作为本地时间硬件抽象层,local_time—hw_device结构
得到时间的设备,如下所示:
struct local time h w d e v i c e {
struct h w device t common;
int64_t (*get_local_time) (s七ruct local_time_hw_device* dev); //获取时间
u i n t 6 4 _ t (*get_local_freq) (struct local_time_hw_device* dev); //获取频率
int (*set_local_slew) (struct local_time_hw_device* dev, II设消转换频率
J.吐16_t rate);
int (*get_debug_log) (struc七local_time_hw_device* dev, II获取调试的Log
struct local_time_debug_event* records, int max_records);
);

local_time的硬件抽象层用于获取时间,获取频率和设置转换频率。如果有硬件支持,
可以从特定的硬件得到更准确的信息。这种方式不同于Linux使用标准系统调用到的方式,
也不同于Android中使用Alarm设备调用RTC。

24.3 本地时间 B SP 的实现

通用的本地时间实现
Android系统包括了默认的本地时间实现,其代码路径为libhardware/modules/local_

373�
•oo Android板级支持与硬件相关子系统

time, 只有 一 个源代码文件 local_tim e_hw .c, 其中的内容将生成local_time.default.so, 放置


在目标系统的/system/lib/hw目录中。
get_local—time函数指针是关键,实现如下所示:
static int64 t ltdev get local time (struct local time h w device* dev) {
struct timespec ts;
uint64_t now;
int ret;
r e 七 = c l o c k gettime(CLOCK_MONOTONIC, &ts); //获取事件的处理
II省略错误处理的内容
now = (((uint64_t)七s.tv_sec) * lOOOOOOOOOull) + ((uint64一七) ts.tv_nsec);
return (int64一七) now;

clock_gettime()是C语言中标准的获得时间的函数,也就是说在没有使用特别的硬件的
情况下,时间只需要通过标准的方式调用。
get_local_freq函数指针的实现如下所示:
static u i 吐 6 4 t l t d e v _ g e t _ l o c a l _ f r e q ( s t r u c t local—七ime_hw_device* dev) {
return lOOOOOOOOOull;

函数实现返回的是64位的常量,表示1GHz。
在模块的打开函数ltdev_ open()中,将以上两个函数指针赋值到建立的stub local
time_device结构中,没有实现set_local_slew函数指针赋值为NULL。

�374
第25章
Android 4.2密钥

25.1 密钥概述

密钥是Android 4.2新增的 一 个硬件抽 象层模块 ,用 于支待 A ndroid 安全 系统 的运 行 。


密钥部分本身包括了生成密钥对(公钥和私钥)、使用密钥签名数据、使用密钥验证数据
等功能。
在Android 4.2版本中,增强了安全系统相关的内容,密钥相关的部分从通用的代码中
被分出,按照Android标准硬件模块的方式定义接口,称之为keymaster。密钥部分实现后,
由通用的代码部分打开密钥硬件模块,并调用其实现自身的功能。
Android安全系统和密钥相关部分的相关内容如表25-1所示。

表25-1 Android 安全系统和密钥相关部分的相关内容


Android的层次 音频系统部分 描 述
底层 基千不同算法的实现 一般不具有特别的硬件
硬件抽象层 keymaster硬件模块 基千软件和硬件实现
本地框架层 keystore守护进程,keystore_client 使用了Socket与本地层和Java层通信
Java层 android.secunty包中的KeyStore类 使用Socket与低层实现通信


Android安全系统的 个重要部分是 keystore, 它作为安全存储的手段,其中就需要使
用到密钥的内容。keystore在Android的早期版本就存在,其结构在各个Android版本中差
别也不大。主要区别就在千Android 4.2之前的版本中是直接调用算法库,而在Android 4.2
中则调用密钥的硬件模块。
密钥既然是 一 个硬件模块 ,那么在各个不 同的系统 中就允许有 不 同的实现 ,让各个 系

统可以在不改变核心代码的情况下,有选择地使用不同的加密手段。 般情况下 ,这种 实
“ ”
现上的差别不是体现在真正不同的 硬件 上面 ,而是各种加密算法上 ,例如 D SA (NIST
标准)、RSA (由建立标准的三个人名字的首字母)等。
• O O Android板级支持与硬件相关子系统

25.2 安全和密钥子系统结构

�25.2.1 安全和密钥的总体结构
Android密钥相关的部分包括keymaster硬件模块、keystore守护进程、keystore _ client
库,以及Java层的KeyStore类。
Android的密钥相关部分基本结构如图25-1所示。

android .security .KeyStore


Java框架层

llbkeystore

keystore client

keystore 守护进程

keymaster 适配层接 口
······-····

keymaster
本地框架层


图25-1 Android的密钥相关部分结构

e hardware/libhardware/include/hardware/keymaster.h: 硬件抽象层的接口。
keystore相关的内容在system/security/目录中,包括以下几个目录。
e keystore: 生成keystore可执行程序以及libkeystore _ client.so库。
e keystore-engine : 生成libkeystore.so库,供本地程序调用。
KeyStore的相关代码在android.security包中。
e frameworks/base/keystore/java/android/security/KeyStore.java。

�25.2.2 keystore守护进程
keystore是提供Android系统安全机制相关功能的可执行程序,提供安全存储的功能,
在Android系统中作为守护进程通信,在设备的目录/ dev /socket/中的keystore文件,为Socket
类型,作为keystore与上层通信的手段。
keystore守护进程监听名称为"keys tore"套接字,作为与客户端通信的手段,客户端
可以是本地程序,也可以是Java程序。命令的格式是单字符的格式:'i'表示插入,'d'表示
删除,'m'表示导入密钥对,'n'表示签名数据。

�376
第 25 章 Android 4.2 密 钥 o o e

keystore使用了keymaster硬件模块作为生成键和加密的手段,初始化过程如下所示:
static int keymaster_device_ini巨alize(keymas七er_device_t** dev) {
int re;
const hw module t* mod;
r e = hw get module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);//获得库
// 省略错误处理内容
r e = keymaster open(mod, dev); //打开 keymaster 模块
// 省略错误处理内容
return 0;

keystore的main()函数中,进行了监听和建立类的操作,如下所示:
keyrnaster device t* dev;
if (keyrnaster device initialize (&dev)) { return 1; } / /初始化keyrnaster设备
if (listen(con七rolSocket, 3) == -1) { return l; }

-- ----.-- ,_一
signal(SIGPIPE, SIG_IGN);
KeyStore keys七ore (&entropy, dev)· / / f f l )5. Y._S to re_.:类
建立可

KeyStore类提供程序的主要执行机制,这个类把keymaster设备作为mDevice保存,
其中的importKey()函数如下所示。
keymaster_device_t* mDevice;
ResponseCode importKey(const Value* key, const char* filename) {
uint8_t* data;
size_t dataLength;
int re;
II 省略错误处理
r e = mDevice->import_keypair(mDevice, //调用 keym aster_ dev ice主 导入密钥
key->value, key->length, &data, &dataLength);
// 省略错误处理
Blob keyBlob(data, dataLength, NULL, 0, TYPE_KEY_PAIR);
free(da七a);
return put(filename, &keyBlob);

与 之 类 似 , K e y S t o r e 类 中 几 个 函 数 通 过 调 用 K e y m a s t e r_device_t 实 现 , 如 下 所 示 。
• generate(): 生 成 密 钥 对 , 调 用 g e n e r a t e _keypair函数指针。

• get_pubkey(): 从 密 钥 对 中 得 到 公 钥 , 调 用 g e t _keypair_public函数指针。
• del_key(): 删 除 密 钥 对 , 调 用 d e l e t e _keypair函数指针。
• sign(): 使 用 密 钥 签 名 数 据 , 调 用 s i g n — d a t a 函 数 指 针 。
• verify(): 使 用 密 钥 验 证 签 名 的 数 据 , 调 用 v e r i f y _data函数指针。

�25.2.3 android.security的内容
android.security包中的KeyStore类不是APL而是使用@hide注释隐藏类,主要供框
架层和系统应用使用。
KeyStore类的主体部分,以及核心的execute()方法如下所示:

377�
•oo Android板级支持与硬件相关子系统

m E r r o r = PROTOCOL_ERROR;
f o r (byte [ J parameter : parameters) { / /再循环中处理参数
i f ( p a r a m e t e r = = n u l l 11 p a r a m e t e r . l e n g t h > 6 5 5 3 5 ) l r e t u r n n u l l ; }

L o c a l S o c k e t s o c k e t = new L o c a l S o c k e 七 ( ) ; //打开本地的套接字
try {
socket.connect(sAddress);
O u t p u 七 S t r e a m o u 七 = s o c k e t . g e t O u t p u t S t r e a m ( ) ; //获得套接字的输出流
out.write(code);
f o r (byte[) p a r a m e t e r : parameters) { //向套接字写入参数的内容
o u t . w r i t e { p a r a m e t e r . l e n g t h >> 8 ) ;
out.write(parameter.length);
OU七.write(parameter);

out.flush();
s o c k e t . s h u t d o w n O u t p u t () ; / /直接关闭输出的套接字
InputStream i n = socket.getinputStream(); //获得套接字的输入流
i f ( ( c o d e = i n . r e a d ( ) ) ! = NO_ERROR) ( //读取返回值
if (code ! = - 1 ) ( m E r r o r = c o d e ; )
return null;

A r r a y L i s t < b y t e [ ] > v a l u e s = new A r r a y L i s t < b y t e [ ] > ( ) ; //作为返回值


while (true) { //循环读取返回的信息
int i, j;
·
辽((i = in.read()) = = -1) { b r e a k ; ) / / 第 1 字节
if ( ( j = i n . r e a d ( ) ) == - 1 ) { r e t u r n n u l l ; ) · 月 第 2 字节
b y t e [ ] v a l u e = new b y t e [ i << 8 I j ] ; //获得总长度,并建立数组
f o r ( i = 0 ; i < v a l u e . l e n g t h ; i += j ) {
i f ( ( j = i n . r e a d ( v a l u e , i , v a l u e . l e n g t h - i ) ) == - 1 ) {
return null;


values.add(value); //将 个 b y t e [ ] 加 入 Ar r a y L i s t <b y t e [ ] >

m E r r o r = NO_ERROR;
return values;

II 省略错误处理的内容
return null;

executeQ方法用于与keystore套接字进行远程的通信和交互,执行的流程是:打开套接
字,向其写入输入的参数,然后再读取其中的返 回,以byte[]类型的ArrayList返回。此处
直接由Java对本地的套接字进行处理,上下层 的数据格式相对应。
几个功能性的方法都通过executeQ方法来实现,如下所示:
p r i v a t e b y t e [ J s i g n ( b y t e [ J keyNarne, b y t e [ J d a t a ) {
f i n a l A r r a y L i s t < b y t e [ ] > v a l u e s = e x e c u t e ( ' n ' , keyNarne, d a t a ) ;
r e t u r n ( v a l u e s = = n u l l I I v a l u e s . i s E m p t y ( ) ) ? n u l l : v a l u e s . g e t (0) ;

p u b l i c b y t e [ ] s i g n ( S t r i n g key, b y t e [ ] data) {
return sign(getKeyBytes(key), data);

p r i v a t e boolean importKey(byte[) keyName, b y t e [ ) key) (


e x e c u t e ( ' m ' , keyName, k e y ) ;
r e t u r n m E r r o r 二 乏_NO ERROR;

�378
第25章 Android 4.2密钥 ooe

p u b l i c b o o l e a n imper七Key(String keyName, byte[] key) {


return importKey(getKeyBytes(keyName}, key);

除了表示命令码的n'、'm等字符之外,其他的参数作为execute()方法的参数列表传入。
但sign()等方法具有返回值,而importKey()等方法没有返回值,
android.security包中的AndroidK.eyStore、AndroidKeyPairGenerator等类均调用了
KeyStore, 它们构成了Java框架层的安全系统。

25.3 密钥的BSP部分的结构

Android 4.2使用keymaster.h作为密钥部分硬件抽象层,包括了密钥的生成、管理、签
名数据和验证数据等功能。一些基本类型的定义如下所示:
enum { K E Y M A S T E R _ S O F T W A R E _ O N L Y = Ox00000001,};
struct keystore_module { hw_module_t common; }; //模块的定义
typedef e n u m { T Y P E _ R S A = 1, } keymaster_keypair_t; //定义密钥对的类型
七ypedef struct { //表示一些参数
uint32_t modulus_size;
u i n t 6 4 _ t public_exponent;
} keymaster_rsa_keygen_params_t;
typedef e n u m { D I G E S T _ N O N E , ) keymaster_rsa_digest_t;
typedef e n u m { PADDING_NONE, }

keymaster _ device是keyinaster硬件抽象层的核心部分,定义的内容如下所示:
struc七keymaster device { //表示密钥的设备
struct h w d e v i c e t common;
uint32 t client version;
u i n t 3 2 _ t flags;
void* context;
int (*generate keypair) (const struct keymaster device* dev, //生成密钥对
cons七keymas七er_keypair_t key_type, const void* key_params,
uint8_t** key_blob, size_七* key_blob_length);
int (*import keypair) (cons七struct keymaster device* dev, //导入密钥
const uint8_t* key, c o n s 七 s i z e _ t key_length,
uint8_t** key_blob, size_七* key_blob_length);
int (*get keypair public) (const struct keyrnas七er device* dev, //获得公钥
c o n s 七 u i n t 8 _ t * key_blob, const size_t key_blob_length,
uint8_t** x509_data, size_t* x509_data_leng七h);
int (*delete_keypair) (const struct keymaster_device* dev, II删除密钥对
const uint8_t* key_blob, c o n s 七 s i z e _ t key_blob_length);
int (*delete_all) (cons七struct keymaster_device* dev); II删除所有
int (*sign_data) (const struct keymaster_device* dev, , II签名数据
const void* signing params,
const uint8_t* key_blob, const size_t key_blob_length,
const ui吐8一七* data, const size_t data_length,
uint8_t** signed_data, size_t* signed_data_length);
int (*verify_data) (cons七struct keymaster device* dev, //验证数据
const void* signing_params,
const uint8 t* key blob,const size t key b l o b leng七h,
const uintB_t* signed_data,const size_t signed_data_length,
const uintB t* signature, const size_t signature_length);
};

379�
• O o Android板级支持与硬件相关子系统

t y p e d e f s t r u c t keYf!!旦 s t e r _ d e v i c e keymaste王啊_ d ev i c e_ t ;

各个函数指针中凡是名为key_blob的变量,都表示密钥本身,使用指针的方式指向
原始的数据,因此需要附加参数key—blob_length作为数据区的长度。几个函数指针的功
能如下所示。
f t generate—keypair和import_keypair: 分别是根据类型和密钥建立key—blob, 也就产生

了key_blob的数据区,并将指针的地址和长度返回。
a get_keypair_public: 从key_blob中获得保存公钥的X509证书。
e delete_keypair: 删除 一 个 key_blob。
a sign_data: 根据参数signing_params使用key—blob签名数据,签名后数据将于独立
的内存中建立,并将指针的地址和长度返回。
e verify_data: 根据参数signing_params使用key_blob验证数据。

25.4 密钥的BSP实现

迪25.4.1 通用的软件密钥实现
Android的通用代码中包括了keymaster的软件实现,作为密钥硬件模块的默认实现。
其代码路径为security/softkeymaster/, 其中只有keymaster_openssl.cpp 一 个源文件,将生成
keystore.default.so动态库,放置在目标系统的/system/lib/hw目录中。此种实现方式使用了
为OpenSSL提供相关算法的libcrypto库。
keymaster_openssl.cpp中的wrap_keyO函数的实现如下所示:
static int wrap_key(EVP_PKEY* p k e y , i n t t y p e ,
u i n t 8 _ t * * keyBlob, s i z e _ t * keyBlobLength) {
1.nt p u b l i c L e n = i 2 d _ P u b l i c K e y ( p k e y , N U L L ) ; //获得公钥长度
i n t p r i v a t e L e n = i 2 d _ P r i v a t e K e y ( p k e y , NULL); //获得私钥长度
// 省略错误处理
*keyBlobLength = g e t s o f t k e y header s i z e ( ) + s i z e o f ( i n t ) //表示 k e y Bl o b 的长度
+ sizeof(int) + privateLen + sizeof(int) + publicLen;
UniquePtr<unsigned c h a r [ ] > derData(new unsigned char[*keyBlobLength]);
// 省略错误处理
unsigned char* p = derData.get ( ) ;
p = add_softkey_header(p, *keyBlobLength); //写入头信息
f o r ( i n t i = s i z e o f ( i n t ) - 1 ; i >= 0 ; 一
i 一) ( //分配内存
* p + + = ( t y p e > > ( 8 * i ) ) & OxFF;

for ( i n t i = s i z e o f ( i n t ) - 1 ; i > = O; 一 i 一) { //将公钥数据写入内存
* p + + = ( p u b l i c L e n >> ( 8 * i ) ) & OxFF;

if ( i 2 d _ P u b l i c K e y ( p k e y , &p) ! = p u b l i c L e n ) { r e t u r n - 1 ; )
for (inti= s i z e o f ( i n t ) - 1 ; i > = 0 ; 1 一- ) { //将私钥数据写入内存
* p + + = ( p r i v a 七 e L e n >> ( 8 * i ) ) & OxFF;

if ( i 2 d _ P r i v a t e K e y ( p k e y , &p) ! = p r i v a t e L e n ) { return -1; }
*keyBlob = derData.release(); I I 将 指针返 回
r e t u r n 0;

�380
第 25 章 Android 4.2 密钥 oo•

wrap_key() 用于生成 k ey B lo b , 首先分配 k ey B lo b 的 内存 ,其结构包括头信息 、私钥长


度和私钥数据、公钥长度和公钥数据等,这些内容按顺序写入。
u n w r a p _ key() 函数 的实现与 w rap _ k ey ()相 反。

用于签名的 o p en ssl_sign_ data() 函数如下所示 :


S七atic int openssl_sign_data(const keyrnaster_device_七* dev,
const void* params,
const uint8_t* keyBlob, const size_t keyBlobLength,
const uint8_t* data, const size_t dataLength,
uint8_t** signedData, size_t* signedDataLength) {
int r e s u l t = -1;
EVP M D CTX ctx;
size t maxSize;
II 省略错误处理
Unique_EVP_PKEY pkey(unwrap_key(keyBlob, keyBlobLength));
if (pkey.get() == NULL) { return -1; }
if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) { return -1; }
keymaster_rsa_sign_params_t* sign_params
= (keyrnaster_rsa_sign_params_t*) params; JI取出参数结构
II 省略错误处理
Unique_RSA rsa (EVP_PKEY_getl_RSA (pkey. get(}}}; //得到RSA的私钥
if (rsa.get (} == NULL} ( r e t u r n -1; }
UniquePtr<uint8_t> signedDa七aPtr( //对数据进行签名
reinterpret_cast<uintS_t*> (malloc(da七aLength)));
II 省略错误处理
unsigned char* tmp = reinterpret_ cast<unsigned char*> (signedDataPtr. get()) ;
if (RSA_priva七e_encryp七(dataLength, data, tmp, rsa.get (), RSA_NO_PADDING)
<= 0) { return -1; ) //使用私钥进行加密
*signedDataLength = dataLength; //返回数据的长度和数据区指针
*signedData = signedDataPtr.release();
return 0;

此函数就被设置为 k ey m a ster 设备 的 sig n _ d ata 函数指针 ,对数据 的签名过程是通过调


用O p en S S L 的函数完 成 的。
keymaster 硬件抽象层 的 g en erate_ keypair 、im p o rt_ keypair, verify_ data 等几个 函数指针
的实现与 sig n _ d ata 类似 ,由于并没有硬件 的上下文维护密钥对 的存储 ,因此 d elete_ keypa订
和 d e lete_ keypair 两个 函数指针 为空实现 。

�25.4.2 Galaxy Nexus的密钥实现


Galaxy N e x u s 系统 的密钥使用 T u n a 板 的实现 ,虽然没有特 别 的硬件支持 ,但使 用 了单
独的实现。其代码的路径为 d e v ice/sa m su n g /tu n a/k ey m a ster , 其中只有 一 个 k ey m a ster_ tuna.c
源代码文件,将生成 k ey sto re .tuna 动态库 。
PKCS#ll 表示公钥加密标准 ( P u b lic-K ey Cryptography Standards) 。P K C S # ll (简称为
P11) 是 PK C S 的标准 ,共发布 了 70 多条指令 ,例如 C _ OpenSession 、C _ CreateObject 、
C _ CreateObject 等 ,可 以跨平 台使用 。
Tuna 板 密钥硬件抽象层 的实现 中,调用 了 lib cry p to 和 lib tf_ crypto _ sst 库 ,并包 含 了
crypto虹h 和 p k c s l l.h 头文件 。

381�
•oo Android 板级支持与硬件相关子系统

find—single_ objectO 、k ey b lo b _ sav e() 、k ey b lo b _ re sto 叫 )


等是工具 函数 ,是对 P K C S 功 能
的封装,而各个 k e y m a ster 设备的实现 函数指针均 以 tee_ 函数为开头 。其 中使用 的 "C_ "
开头的函数和 "C K _ " 开头的类型均来 自 P K C S# ll 标准 。
用于生成密钥对的 tee_generate_ k e y p a 订 O 函数如下所示 :

static int tee_genera七e_keypair(const keymaster_device_t* dev,


const keymaster_keypair_t type, c o n s t void* key_params,
uintB t** key blob, size t* k e y b l o b length) {
C R B B O O L b T R U E = C K TRUE;
//省略错误处理
keymaster rsa keygen p a r a m s t* rsa params
= (keymaster_rsa_keygen_params_七*) key_params;
C K M E C H A N I S M m e c h a n i s m = { C R M R S A PKCS R E Y PAIR GEN, NULL, 0 ' , }
C R _ U L O N G modulusBi七s = (CK_ULONG) rsa_params->modulus_size;
CK_BYTE publicExponent[sizeof(uint64一七)];
const u i n t 6 4 _ t e x p = rsa_params->public_exponent;
size_七offset = sizeof(publicExponent)一l;
for (size_七i = 0; i < sizeof (publicExponent); i++) {
publicExponent[offset--J = ( e x p > > (i * CHAR_BIT)) & OxFF;

U n i q u e _ B y t e A r r a y obj Id (generate_random_id ());


if (objid.get () = = NULL) { return -1; }
C K A T T R I B U T E publicKeyTemplate[] = { //公钥的模板
{CKA ID, obj Id->get (), obj Id->length ()},
{CKA_TOKEN, &bTRUE, sizeof (bTRUE)},
{CKA_ENCRYPT, &bTRUE, 豆 z e o f (bTRUE) } ,
{CKA_VERIFY, &bTRUE, sizeof (bTRUE)},
{CKA_MODULUS_BITS, &modulusBits, sizeof (modulusBits)},
{CKA_PUBLIC_EXPONENT, publicExponent, sizeof(publicExponen七)),
);
C K A T T R I B U T E privateKeyTemplate[] = ( //私钥的模板
{CKA ID, obj Id->get (), obj Id->length () } ,
{CKA_TOKEN, &bTRUE, sizeof (bTRUE)},
{CKA_DECRYPT, &bTRUE, sizeof (bTRUE)},
{CKA_SIGN, &bTRUE, sizeof (bTRUEJ},
};
C r y p t o S e s s i o n session(reinterpret_cast<CK_SESSION_HANDLE>(dev->contex七));
C K _ O B J E C T _ H A N D L E hPublicKey, hPrivateKey;
C K R V r v = C GenerateKeyPair(session.get(),&mechanism, //生成密钥对
publicKeyTemplate, sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE),
privateKeyTemplate, sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE),
&hPublicKey,&hPrivateKey);
if (rv != CKR_OK) { return - 1 ; )
O b j e c t H a n d l e publicKey(&session, hPublicKey); //保存公钥的句柄
O b j e c t H a n d l e privateKey(&session, hPrivateKey); II保存私钥的句柄
r e t u r n keyblob_save(objid.get(), key_blob, key_blob_length);

tee_generate_keypair ()函数调用了 C _ Generate K e y P a 试)


函数生成密钥对 ,私钥和公钥 的
模板作为参数传入。
用于签名数据的 tee_ sign_data() 函数如下所示 :

static int te e _ s i g n _ d a t a ( c o n s t keymaster_device_t* dev, const void* params,


const uintB_t* key_blob, const size_t key_blob_length,
const uint8_七* data, const size_t dataLength,
uin七8 t** signedData, size_t* signedDataLength) {
// 省略错误处理

�382
第25章 Android 4.2密钥 ooe
CryptoSession session(reinterpret_cast<CK_SESSION HANDLE>(dev->context));
ObjectHandle publicKey (&session); //得到公钥的对象句柄
Obj e c t H a n d l e p r i v a t e K e y (&session) ; / /得到私钥的对象句柄
// 省 略 错 误 处 理
江(keyblob_restore(&session, key_blob, key_blob_length, //得到公钥和私钥
&publicKey,. &priva七eKey)) { return -1; }
keymas七er_rsa_sign_params_t* sign_params
= (keymaster_rsa_sign_params_七*) params;
II 省略错误处理
CK MECHANISM rawRsaMechan1.sm = { CKM RSA X 5 0 9 , NULL, 0 } ;
CK_RV r v = C _ S i g n i n i 七 ( s e s s i o n . g e t ( ) , & r a w R s a M e c h a n i s m , p r i v a t e K e y . g e t ( ) ) ;
if ( r v != CKR OK) { r e t u r n - 1 ; }
CK BYTE s i g n a t u r e [ 1 0 2 4 ) ;
CK_ULONG s i g n a t u r e L e n g t h = 1 0 2 4 ;
r v = C _ S i g n ( s e s s i o n . g e t ( ) , da七a, d a 七 a L e n g t h , //调用进行签名
signa七ure, & s i g n a t u r e L e n g t h } ;
i f ( r v != CKR_OK} { r e t u r n - 1 ; }
U n i q u e P t r < u i n t 8 _ t [ ) > f i n a l S i g n a t u r e ( n e w uint8一七[signa七ureLength)};
i f ( f i n a l S i g n a t u r e . g e t ( } == NULL} { r e t u r n - 1 ; }
rnerncpy(finalSignature.get(), signature, signatureLength}; / / 复 制 签 名 本 身
*signedData = f i n a l S i g n a t u r e . r e l e a s e ( } ; //将签名后的数据的指针和长度返回
*signedDataLength = static_cast<size_t>(signatureLength);
r e t u r n 0;

用千验证数据的tee_verify_data ()函数如下所示:
static i n t t e e _ v e r i f y _ d a t a ( c o n s t k e y m a s t e r _ d e v i c e _ t * dev, c a n s t v o i d * params,
c a n s t u i n t 8 _ t * k e y B l o b , cons七size-:--t k e y B l o b L e n g t h ,
c a n s t u i n 七 S _ t * signedDa七a, c a n s t s i z e _ t s i g n e d D a t a L e n g t h ,
canst u i n t 8 t * signature, canst size t signatureLength) {
·
// 省 略 前 面 的 部 分 , 与 t e e s i g n d a t a ( ) 函 数 的 实 现 基 本 相 同
CK MECHANISM r a w R s a M e c h a n i s m = { CKM RSA X 5 0 9 , NULL, 0 )
CK_RV r v = C _ V e r i f y i n i t ( s e s s i a n . g e t ( ) , & r a w R s a M e c h a n i s m , p u b l i c K e y . g e t ( ) ) ;
i f ( r v != CKR_OK) { r e t u r n - 1 ; )
r v = C_ V e r i f y ( s e s s i a n . g e t ( ) , s i g n e d D a t a , s i g n e d D a t a L e n g t h , / /进行验证
canst_cast<unsigned char*>(signa七ure), signatureLength);
i f ( r v != CKR_OK) { r e t u r n - 1 ; )
r e t u r n 0;

tee—sign—data()和tee_verify_data()拍数执行的流程类似,分别调用了C_ Signlnit()、
C_Sign()、C—Verifylnit()、C_Verify()等函数,完成了签名和验证。

383�
第26章
电源管理

26.1 Android电源管理

Android的电源管理结合了Linux内核和用户空间控制。主要包括了内核中的核心机制
和用户空间策略方面的控制。
Android和标准Linux的电源管理的状态差异如图26-1所示。

Normal Runin
Android Kernel PM

Suspend Statu
INT

图 26- 1 Android和标准Linux的电源管理状态

Linux电源管理结构是以CPU为核心的,具有正常运行和挂起(Suspend)两个状态。
在进入Suspend的过程中,内核将调用各个驱动的Suspend (例如平台驱动platform—driver
中的Suspend函数指针),由此各个驱动程序关闭自己的硬件,降低功耗,随后CPU也停
止运转,用户空间可以软件控制进入Suspend。当需要恢复时,通过特殊的硬件中断,例如
电源按键的引脚激活CPU, 然后调用各个驱动的resume函数(例如平台驱动platform_driver
中的resume函数指针),然后进入正常运行状态。
一 一
因此,在Linux系统的电源管理过程中 个实际的 情况是 :Suspend是 个连续 的过程 ,
第26章电源管理 ooe

一旦过程进行,最终的结果就是各个硬件挂起并且CPU停止运转。由此,如果没有独立的
控制接口,不能让所有硬件关闭,却不让CPU停转。
Android考虑了硬件系统的情况和现实情况的需求。在很多硬件平台,尤其是在嵌入式
平台中,如果只是CPU运转,并不需要耗费很多电量。因此,有很多时候需要保持大部分
硬件关闭,但是CPU保持运转的状态。
因此,Android在Linux的挂起过程中加入了 一 个状态 ,也就是空闲 Idle 状态 ,这个状
态也称之为早期挂起。挂起的流程是先进入Idle状态,然后再进入Suspend状态,而是否
由Idle状态进入Suspend状态,则由wake_lock的情况决定。
由此,Android进入Suspend时,内核要首先执行各个驱动early_suspend, 完成调用后,
再调用各个驱动的Suspend, 然后CPU再休眠。正常运行状态进入early_suspend状态或者
early_suspend状态进入Suspend状态的执行条件,就是没有wake_lock锁;在恢复阶段,
则先调用各个驱动的resume函数,再调用各个驱动的later_resume函数。

今 提 示 : Andro过电源管理的核心理念是将挂起的流程一分为二,分别处理。

Android控制进行Suspend的流程调用实际上与标准Linux相同,但是执行的结果可能
不 同 。 一 个 最 常 用 的 场 景 就 是 各 个 模 块 对 从 e a r l y _s u s p e n d 进 入 S u s p e n d 的 过 程 上 了
wake_lock锁,由此,大部分硬件模块挂起后,CPU和某些少数模块还可能运行。
从具体平台的实现上,除了Android对Linux内核的公共改动之外,要做的就是为需
suspend 和 later—
要单独控制的硬件加入early— resum e 的执行 ,以及在用户 空 间使用加入
wake_lock控制电源管理的流程。通常情况下,early_suspend和later_resume的实现分别类
似于Suspend和resume, 或者执行更少的操作,而加入wake—
lock 就是阻止进入挂起状态 。

26.2 Android内核空间的电源管理

电源管理的改动是Android对标准的Linux内核做出最大改动。电源管理的改动是全
局的,主要由wakelock和earlysuspend两个部分组成。

汕26.2.1 总体结构
wakelock是Android提供的 一 种特殊 的机制 ,用于请求 C PU 资源 。当持有 w akelock
之后,可以阻止系统进入暂停或其他低功耗状态。
include/linux/目录中wakelock.h和earlysuspend.h是Android电源管理的头文件。
kernel/power/目录中包括几个实现源文件,如下所示。
• wakelock.c: wakelock的核心实现。
e userwakelock.c: 在用户空间使用wakelock的接口。
a earlysuspend.c: earlysuspend的核心实现。
• fbearlysuspend.c: 为FrameBuffer驱动实现的earlysuspend。
• consoleearlysuspend.c: 为控制台驱动实现的earlysuspend。

385�
• O o Android板级支持与硬件相关子系统

Android电源管理的内核部分的结构如图26-2所示。

用户空间程序调用

阁/'f)ON8tlwa阳:...lod< 用户空间
./'f)ON8tlwa屈_unlod<
内核空间
内核对wake_lock的调用 其他使用earlysuspend的驱动

注册和调用

图26-2 Android电源管理的内核部分

迪26.2.2 wakelock

wakelock提供 种 对 S u sp en d 加 锁 的 机 制 , 其 实 现 需 要 P M 和 R T C 模 块 的 支 持 。
wakelock.h头文件中定义的wakelock的枚举类型如下所示:
enum {
WAKE LOCK SUSPEND, //阻止进入Suspend挂起状态
WAKE LOCK IDLE, //阻止进入Idle空闲状态
WAKE LOCK TYPE COUNT
};

枚举表示了wakelock的两种类型: WAKE_ LOCK_ SUSPEND用于阻止系统进入


Suspend状态(CPU挂起),一般是深度休眠,Android提供了通用实现; W A K E _ L O C K _ I D L E
用于阻止系统进入idle状态(CPU低功耗运转)。
w a k e _ l o c k 结 构 是 用 于 表 示 一 个 w ak elo ck 锁 的 句 柄 , 如 下 所 示 :
struct wake_lock {
#ifdef CONFIG HAS WAKELOCK
struct list_head link; II用于支待各个wake 一lock链表
int flags; //锁的标志(活动状态 、超时等信息)
canst char *name; //锁的名称
unsigned long expires; II用于超时的处理
#ifdef CONFIG WAKELOCK STAT
struct {
int count;
int expire_coun七;
int wakeup count;
ktime t total time;
ktime t prevent suspend七ime;
ktime t max time;
ktime t last time;
} stat; //用于wake_lock的统计
#endif

�386
第26章电源管理 ooe

#endif

CONFIG_ WAKELOCK_STAT 是 一 个关于 w ak e_ lo ck 统计 的宏 ,如果这个宏被使能将


在 P ro c 文件系统 的/p ro c/w ak e lo c k s 目录 中提供系统 中 w a k e_ lo c k 的相关信息 。
w a k e l o c k . h 头文件 中的 w a k e lo ck 的相关 函数如下所示 :
v o i d w a k e _ l o c k _ i n i t ( s t r u c 七 w a k e _ l o c k *lock, int七ype, canst char *name);
v o i d w a k e _ l o c k _ d e s t r o y ( s t r u c t w a k e _ l o c k *lock);
v o i d w a k e _ l o c k ( s t r u c t w a k e _ l o c k *lock);
void wake_lock_tirneout(struct w a k e _ l o c k *lock, long timeout);
v o i d w a k e _ u n l o c k ( s t r u c t w a k e _ l o c k *lock);

几个函数可以在内核中被调用。 w a k e_lock_initO 函数和 w ak e_ l o c k _destroyO 函数相对



应,用于创建和销毁 个 w ak e lo ck ; w a k e _ l o c k O 函数和 w ak e_ u n l o c k O 函数相对应 ,用于持
有和释放 一 个 w a k e lo ck ; wake_lock_timeout() 函数表示持有

个带超 时机制 的 w ak e_ lo c k ,

会在超时后自动解锁。锁根据字符串名称建立。
两个用于查询的 w ak e_ lo c k 函数如下所示 :
i n 七 w a k e _ l o c k _ a c t i v e ( s t r u c t w a k e _ l o c k *lock); //判断wake_lock是否锁住
long h a s _ w a k e _ l o c k 伈 n t type); //查看某个类型的wake_lock是否具有

w a k e _ l o c k _ a c t i v e O 函数用于查看某个 w a k e_ lo ck 是否在上锁状 态 ,超 时的锁将被视 为


没有被锁; h a s _w a k e_lock() 函数用千查看某种类型的所有锁是否被待有 。
wakelock.c 中的 w a k e_ lock_ i n t e m a l O 函数是 w ak e—lo ck O 和w ak e_ lock_timeout() 的实现 ,

其主要的片段如下所示:
static v o i d w a k e _ l o c k _ i n t e r n a l ( s t r u c t w a k e _ l o c k *lock,
long timeout, int has_timeout) {
1.nt type;
u n s i g n e d long irqflags;
long expire_in;
s p i n _ l o c k _ i r q s a v e (&list_lock, irqflags}; / / 对 w a k e lock锁的链表上自旋锁
type = lock->flags & WAKE_LOCK_TYPE_MASK;
B U G _ O N ( t y p e > = WAKE_LOCK_TYPE_COUNT);
B U G O N (! (lock->flags & WAKE_LOCK_INITIALIZED));
II 省略:统计信息的生成(包含于CONFIG_WAKELOCK_STAT宏之内)
if (! (lock->flags & WAKE_LOCK_ACT工VE)) {
lock->flags I= WAKE_LOCK_ACTIVE;
// 省略:统计信息的生成(包含于CONFIG_WAKELOCK_STAT宏之内)

list_del (&lock->link}; II从链表中去除这个wake_lock锁


if (has_timeout} ( //超时锁的特殊处理
lock->expires = j i f f i e s + timeout;
lock->flags I= WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link, &active w a k e locks [type]}; //增加入链表尾
} else ( / /非超时锁的特殊处理
lock 今 e xp i re s = LONG_MAX;
lock->flags &= -WAKE L O C K A U T O EXPIRE;
list_add(&lock->link, &active_wake_locks[type]); JI增加入链表

if (type = = W A K E _ L O C K S U SP E N D ) ( //对WAKE_LOCK_SUSPEND类型的单独处理
current even七_num++;

II 省略:统计信息的生成(包含千CONFIG_WAKELOCK_STAT宏之内)
辽(has_巨meou七) e x p i r e _ i n = has_wake_lock_locked(type); //查看数目

387�
•oo Android板级支持与硬件相关子系统

else expire_in = -1;


i f ( e x p i r e 1.n > 0) (
mod_timer(&expire_timer, jiffies+ e x p i r e i n ); //修改定时器的设置
) else l
if (del timer(&expire_timer))
-:-
if ( e x p 1 . r e _ i n = = 0) q u e u e _ w o r k ( s u s p e n d _ w o r k _ q u e u e , & s u s p e n d _ w o r k ) ;

spin_unlock irqrestore(&让st lock, irqflags); / / 解 除 wa k e l o c k 锁的链表的 自旋锁

wake_lock 的处理实际上只是数据 结构 的操作 ,所有 的 w ak e_ l o c k 保存在 一 个双 向链表


中,一个比较特殊的处理是没有超时和有超时 w ak e_ l o c k 分别从头部和尾部加入 。

�26.2.3 wakelock的用户空间
wakelock 除 了可 以在 内核 中调用之 外 ,还提供 了对用 户空 间的接 口,此部分 内容在
userwakelock.c 中实现 ,实际上就是各种 内核调用封装 。
wakelock 在 用 户 空 间 的接 口使 用 sy s 文 件 系 统 / sy s/ p o w er / 目录 中 的 w a k e_ l o c k 和
w a k e _u n l o c k 两个常规文件 ,前者用于加锁 (持有 ),后者用于解锁 。例如 ,用户空间对它
们可以进行如下操作:
it c a t / s y s / p o w e r / w a k e _ l o c k #查看现在的锁
PowerManagerService
# echo t e s t l o c k > /sys/power/wake_lock #进行加锁
it c a t / s y s / p o w e r / w a k e _ l o c k #查看现在的锁(多了 t e s t l o c k )
PowerManagerService t e s t l o c k
# echo t e s t l o c k > /sys/power/wake_unlock #进行解锁
# c a t /sys/power/wake_lock #查看现在的锁 ( t e s t l o c k 被取消)
PowerManagerService
it c a t / s y s / p o w e r / w a k e _ u n l o c k #查看解锁的情况
AJ>mComma12ctT坦�a£Key,Even仁s_testlock

wake_lock 在用户 空间的表现形式其实就是字符 串,并且字符 串可 以在用户空间随意建


立,同样的字符串在加锁和解锁过程表示同 一 个对象 。
由于用户空间的接口是 sy s 文件系统 ,因此按照通常 的方式 ,使用 st o r e 和 sh o w 分别
表示它们的控制(写)和显示(读)。
用于控制加锁的 w ak e_ l o c k _ s t o r e ( ) 函数如下所示 :

ssize_t wake_lock_store(struct kobject *kobj, s t r u c t kobj_attribu七e * a t t r ,


c o n s t c h a r * b u f , s i z e _ t n) {
long timeout;
s t r u c t u s e r wake l o c k * l ;
mutex_lock(&tree_lock); //使用互斥霆保护
l = lookup_wake_l?ck_name ( b u f , 1, & t i m e o u t ) ; //根据名称查找锁
if ( I S _ E R R ( l ) ) ( n = PTR_ERR(l); g o t o bad_name; J
江(timeou七) wake_lock_巨meou七(&1->wake_lock, t i m e o u t ) ; //建立有超时的锁
else wake_lock (&1->wake_lock); I I 建立没有超时的锁
bad_name:
mutex_unlock(&七ree_lock); I I 解除互斥爵保护
r e t u r n n;

388
第26章电源管理 oo•

用于控制解锁的wake_unlock_storeO函数如下所示:
ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr,
c o n s t c h a r * b u f , s i z e _ t n) {
s t r u c t user_wake_lock * l ;
mu七ex l o c k ( & t r e e l o c k ) ;
1 = lookup_wake_lock_name(buf, 0, NULL); //根据名称查找锁
if (IS_ERR(l)) { n = PTR_ERR{l); g o t o bad n a m e ; ) ,

wake_unlock(&l->wake_lock); //进行解锁
n o t found:
mutex_unlock(&七ree_lock); //解除互斥贷保护
r e t u r n n;

wake_ lock_storeO和wake_unlock_ store()的核心实现都是调用内核的接口,也包含了从


字符串查找到锁的过程。
wake_lock_sho叫)用于显示当前系统存在的锁的情况,实现如下所示:
s s i z e _ t wake_lock_show(s七ruct kobject *kobj,
s t r u c t kobj_attribute * a t t r , char *buf) {
char *s = buf;
c h a r * e n d = b u f + PAGE_SIZE;
s t r u c t rb_node *n;
s t r u c t u s e r wake l o c k * l ;
mutex l o c k ( & t r e e l o c k ) ;
f o r ( n = r b _ f i r s t ( & u s e r _ w a k e _ l o c k s ) ; n ! = NULL; n = r b _ n e x t { n ) ) { //遍历
l = r b _ e n t r y ( n , s t r u c t user_wake_lock, node);
i f (wake_lock_active(&l->wake_lock))
s + = s c n p r i n t f ( s , e n d - s , "%s " , 1 - > n a m e ) ; //显示名称

s += s c n p r i n t f ( s , e n d - s , "\n"); //结束显示
mutex_unlock(&tree_lock);
r e t u r n (s - b u f ) ;

wake_lock_showO可以用于调试,可以显示出系统中所有持有的锁。按照Android系统
电源管理的原则,只要有 一 个锁存在 ,系统就不会进入 Suspend 状态 。
内核中的wakelock模块维护了wakelock锁的链表,锁可以简单地表示成字符串的形
式。因此,来自内核空间或者用户空间的锁,表示的含义相同。

�26.2.4 earlysuspend部分
earlysuspend其实是 一 个介于正常运行和完全挂起之 间的状态 。在 A ndroid 系统 的 内核
中具有earlysuspend之后,原有的挂起操作并不直接将kernel挂起,而是进入earlysuspend,
直到最后 一 个 w akelock 被释放 ,再进 入原 kernel的挂起流程 。
在用户空间通常通过以下方式查看系统的休眠支持和控制休眠:
#cat/sys/power/state
s t a n d b y mem
# e c h o mem > / s y s / p o w e r / s t a t e

earlysuspend.h定义的earlysuspend相关的结构和函数如下所示:
s t r u c t early_suspend { //表示 一 个 e a r l y_ s u s p e n d 的句柄

389�
• O O Android 板级支持与硬件相关子系统

巨 f d e f CONFIG HAS EARLYSUSPEND


struct list_head link;
in七level;
v o i d (*suspend) ( s t r u c 七 e a r l y _ s u s p e n d * h ) ; / / 实 际 上 表 示 e a r l y_ s u s p e nd
v o i d (*resume) ( s t r u c t e a r l y _ s u s p e n d * h ) ; / / 实 际 上 表 示 l a t e_ r e s ume
i/endif
);
i / i f d e f CONFIG HAS EARLYSUSPEND
v o i d register_early_suspend(struct early_suspend *handler);
v o i d unregister_early_suspend(struct early_suspend *handler);
#else
# d e f i n e r e g i s t e r _ e a r l y _ s u s p e n d ( h a n d l e r ) d o { } w h i l e (0)
# d e f i n e unregister_early_suspend(handler) do { ) w h i l e (0)
i/endif

early_ suspend 是 一 个需要 由内核各个驱动程序进行注册才能生效 的机制 ,当某个驱动


程序通过 register_early_ suspend() 函数 向系统注册 了一 个 early_suspend 之后 ,
其 中的 Suspend
和 resum e 函数指针将分别在挂起之前和恢复之后被调用 。由此 ,就可 以为每 一 个驱动程序
单独实现电源管理的机制。
earlysuspend.c 中通过 内核线程 实现 了 earlysuspend 的核心机制 。线程 定义如下所示 :
static DECLARE W O R K ( e a r l y s u s p e n d w o r k , e a r l y s u s p e n d ) ; // e a r l y _ s u s p e n d 的线程
static DECLARE_WORK(late_resume_work, l a t e _ r e s u m e ) ; // l a t e r e s u m e 的线程
static DEFINE S P I N L O C K ( s t a t e l o c k ) ; _ __ I I控制状态的自旋锁

用于保存 early_suspend 结构 的链表如下所示 :


sta七ic DEFINE_MUTEX(early_suspend_lock); // e a r l y _ s u s p e n d 的互斥怪
S 七 a t i c LIST_HEAD ( e a r t y _ s u s e e n d _ t i a n d l e r s J ; // eaX_ly一邑�spend 结构 的链表

early_ suspend() 函数实现 的主体如下所示 :


static void early_suspend(struct work_struc七*work){
s t r u c t early_suspend *pos;
unsigned long i r q f l a g s ;
i n 七 a b o r t = 0;
mutex_lock(&early_suspend_lock); //对链表的互斥量加锁
spin_lock_irqsave(&state_lock, irqflags); II对控制状态的自旋锁加锁
if (s七ate = = SUSPEND_REQUESTED)state J= SUSPENDED; //设置状态的标志
e l s e a b o r t = 1;
spin unlock irqrestore(&state_lock, irqflags); //对控制状态的自旋锁解锁
// 省略错误的处理
list f o r each e n t r y (pos, & e a r l y suspend h a n d l e r s , link) { //对链表操作
if ( p o s - > s u s p e n d ! = NULL) p o s - > s u s p e n d ( p o s ) ; / / s u s p e n d 操作

mutex u n l o c k ( & e a r l y suspend l o c k ) ; //对链表的互斥登解锁


sys_sync();
abort:
spin_lock_irqsave(&state_lock, irqflags);
i f { s 七 a t e = = SUSPEND_REQUESTED_AND_SUSPENDED) w a k e _ u n l o c k { & m a i n _ w a k e _ l o c k ) ;
spin_unlock_irqrestore{&state_lock, irqflags);

对链表中的每个 early_suspend 调用 Suspend 的过程就是 "early_suspend" 。


late_resume ()函数实现的主体如下所示:
static v o i d l a t e r e s u m e ( s t r u c t work s t r u c t *work) {

390

第26章电源管理 oo•

s t r u c t early_suspend *pos;
unsigned long i r q f l a g s ;
i n t a b o r t = 0;
mutex_lock(&early_suspend_lock); II对链表的互斥蜇加锁
spin_lock_irqsave(&state_lock, irqflags); //对控制状态的自旋锁加锁
if ( s t a t e = = S U S P E N D E D ) s t a t e &= -SUSPENDED; II设悝状态的标志
else abort= l;
spin unlock irqrestore(&state_lock, irqflags); //对控制状态的自旋锁解锁
// 省 略 错 误 的 处 理
l i s t _ f o r _ e a c h _ e n t r y _ 亡 e v e r s e (p o s , & e a r l y _ s u s p e n d _ h a n d l e r s , l i n k ) / / 链 表 操 作
if ( p o s - > r e s u m e ! = NULL) p o s - > r e s u m e ( p o s ) ; / / resume操作
abort:
mu七ex_unlock(&early_suspend_lock}; //对链表的互斥纽解锁

对链表中的每个Late_resume调用resume的过程就是"late_resume"。

汕26.2.5 其他
fbearlysuspend和consoleearlys uspend分别实现了Framebuffer帧缓冲和console控制台
驱动在处理early_ suspend和late_resume时的处理函数。
fbearl ysuspend具有用户空间接口,可以和Android系统框架进行交互,判断是否需要
继续绘制屏幕。/sys/power/目录中的wait_for_ fb_wake和wait_for_fb_sleep文件是只读文件,
可以用于Framebuffer的状态信息。
fbearlysuspend.c中的几个主要函数如下所示:
s t a t i c v o i d stop_drawing_early_suspend(struct early_suspend *h) (
int ret;
unsigned long i r q _ f l a g s ;
spin_lock_irqsave(&fb_state_lock, irq_flags);
f b s t a t e = FB STATE REQUEST STOP DRAWING; II设置标志
spin_unlock_irqrestore(&fb_state_lock, irq_flags);
wake_up_all(&fb_state_wq); I I 唤 醒 一 个线 程 用 于 处理
ret = wa止_event_timeout(fb_state_wq, II等待超时
f b _ s t a t e == FB_STATE_STOPPED_DRAWING, H Z ) ;

s t a t i c v o i d start_drawing_late_resume{struct early_suspend *h) {


unsigned long i r q _ f l a g s ;

spin_lock_irqsave(&fb_state_lock, irq_flags);
f b s t a t e = FB STATE DRAWING OK; //设置标志
spin_unlock_irqrestore{&fb_sta七e_lock, i r q _ f l a g s ) ;
wake_up{&fb_state_wq);

s t a t i c s t r u c t early_suspend stop_drawing_early_suspend_desc = {
. l e v e l = EARLY SUSPEND LEVEL STOP DRAWING,
.suspend = stop_drawing_early_suspend,
.resume = s t a r t _ d r a w i n g _ l a t e _ r e s u m e ,
};

对于Framebuffer而言,early _suspend和late_resume的实现通常通知用户空间,用户
空间得到信息后,就知道应该停止绘制或者可以开始绘制。
consoleearlysuspend实现中early_suspend和late_resume的实现都调用了set_console()
用于控制台的操作。

391�
第26章电源管理 ooe

锁,其实就是关闭了屏幕,而没有让CPU进入Suspend。
写入 II on I在标准 L inux 中是不可能发生的。state 原本的设计是只能休眠 系统 ,不能唤
I

醒,因为休眠之后CPU就停转了,只能等待硬件唤醒。但是在Android系统中,由于休眠
的时候CPU并没有停转,因此还可以软件唤醒。

le 提示 :Android 4.2没有屏幕开关控制。

�26.3.2 电源管理的JNI库
电源管理JNI部分的代码路径是framebases/base/core/jni/android_ os _Power.cpp, 也就是
android.os包中的Power类的本地支持。其中提供了acquireWakeLock、releaseWakeLock、
setScreenState、shutdown和setLastUserActivityTimeout等Java方法的本地支持。
acqu订eW a k e Lock()和ReleaseStringUTFCha忒)两个函数都是通过acqu订e_ wake _lock()和
acquire_wake_ lock()函数完成的。
android—os_Power_reboot()实现用于重新启动,如下所示:
S七atic void android_os_Power_reboot(JNIEnv *env, jobjec 七 clazz, jstring reason) {
sync();
4/ifdef HAVE ANDROID OS
if (reason = = NULL) (
reboot(RB AUTOBOOT); //默认的重新启动
) else (
const char *chars = env 今 GetStringUTFChars (reason, NULL);
reboot(LINUX REBOOT MAGICl, LINUX REBOOT MAGIC2,
LINUX REBOOT CMD RESTART2, (char*) chars); //有参数的重新启动
env 今 ReleaseStringUTFChars (reason, chars); //非正常的处理

JniThrowIOException(env, errno);
#endif

Shutdown的实现实际上也是让系统重新启动。

)提示: Android 4.2的PowerManagerService、EventHub、AudioPolicyService本地部


分直接调用acqu订e_wake—
lock , 并元android.as包中的Power类。

�26.3.3 电源管理的Java部分
android.os包中的Power是 一 个隐藏 的类 ,一般只能 由系统来调用 ,不 由应用程序调用 ,
其代码路径为framebases/base/core/java/android/os/Power.java。
android.os包中的PowerManager类用于电源管理,以POWER_S E R V I C E 为 参 数 调 用
Context类的getSystemServiceO方法可以获得 一 个它 的实例 。
Power Manager的几个主要方法如下所示:
一.
public void goToSleep (long time) II 进入睡眠
public PowerManager.WakeLock newWakeLock(int flags, String tag) II 建立唤醒锁
“ ”
n e w WakeLock()用于建立 一 个 Pow erManager. WakeLock对象,表示 一 个 唤醒锁 ,调

393�
• O O Android 板级支持与硬件相关子系统

用其中的接口可以阻止系统进入低功耗模式。

26.4 电源管理的策略

�26.4.1 驱动程序的变化
为了使用 A ndro id 的 L i n u x 的 ea r l y sy sp en d 机 制 ,
通常需要对 已有 的驱动程序做 出更改 。
这些使用 ea r l y sy sp en d 的更改一 般都包含在宏 C O N F I G _ H A S _ E A R L Y SUSPEND 之 内。
虽然
每个硬件平台驱动的情况不同,但使用 ear l y sy sp en d 机制 的方式类似 。

1. 一个触摸屏的驱动
Synaptics Touchscreen 是一 个使用 12 C 连接到主处理器的触摸屏 ,L i n u x 内核 中为其提
供了 E v en t 设备形式 的驱动程序 。A n d ro id 系统将其驱动进行改动 ,使用 了 ea r l y sy sp en d 机
制。其代码路径为 d r i v e r s/ i n p u t/ t o u c h sc r e en / sy n ap t i c s _ i 2 c _ m u . c 。

在驱动的 probe 过程 中,具有如下代码 :


# i f d e f CONFIG HAS EARLYSUSPEND
t s - > e a r l y _ s u s p e n d . l e v e l = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + l ;
七s->early_suspend.suspend = synap七ics_七s_early_suspend;
ts->early_suspend.resume = synaptics_ts_late_resume;
r e g i s t e r e a r l y suspend(&ts->early_suspend); //注册 一 个 e a r l y s u s p e n d
j/endif

此驱动实现将 ea r l y _ s u s p e n d 类型的句柄保存成驱动结构 中的一 个成员 ,在 p r o b e 阶 段

进行了注册。
挂起和恢复的实现如下所示:
h f d e f CONFIG HAS EARLYSUSPEND
s t a t i c v o i d synaptics_ts_early_suspend(struc七early_suspend *h) {
struct synaptics_ts_data *ts;
t s = container_of(h, s t r u c t synaptics_ts_da七a, e a r l y suspend);
s y n a p t i c s t s s u s p e n d ( t s - > c l i e n t , PMSG SUSPEND); //- 用 s u s p e nd

s t a t i c v o i d synaptics_ts_late_resume(struct early_suspend *h) (
struct synaptics_ts_data *ts;
t s = c o n t a i n e r _ o f ( h , struc七synaptics_ts_data, e a r l y suspend);
synaptics ts resume(七s->client); //调用 r e s ume

#endif
static struct i2c_driver synaptics_ts_dr:iver = {
.probe = synaptics七s probe,
. remove = s y n a p t i c s t s remove,
# i f n d e f CONFIG HAS EARLYSUSPEND
.suspend= synaptics_ts_suspend, //原本的 s u s p e n d
.resume = synaptics_ts_resume, //原本的 r e s ume
#endif
.id table = synaptics ts id,
. d r i v e r = { .name = SYNAPTIC$ I 2 C RMI NAME, },
);

驱动的 e ar l y _ s u s p e n d 和 l a t e_ r e s u m e 实际上就是利用 了 S u sp e n d 和 r e su m e 实现 ,通过

�394
第26章电源管理 ooe

注册的内容将在系统挂起的第 一 个阶段,也就是进入 Idle 状态之前被调用。


2. gpio event驱动
gpio event驱动是为GPIO构建Event设备驱动的模块。Android为此驱动增加了
earlysyspend机制,其代码路径为drivers/input/皿sc/gpio_ event.c。
首先在驱动的数据结构gpio_event中增加了early_suspend句柄,如下所示:
struct gpio_even七{
struct g p i o _ e v e n t _ i n p u t _ d e v s *input_devs;
const struct g p i o _ e v e n t _ p l a t f o r m _ d a t a *info;

--—-
struct e a r l y _ s u s p e n d early_suspend; // e a r l y _ s u s p e n d 类 型的句柄
v o i d *sta七e [0];
) ;

early_suspend和late_resume的实现如下所示:
#ifdef C O N F I G HAS E A R L Y S U S P E N D
v o i d g p i o _ e v e n t _ s u s p e n d ( s t r u c t e a r l y _ s u s p e n d *h) {
s t r u c 七 g p i o _ e v e n t *ip;
ip = container_of(h, struct gpio_event, early_suspend);
g p i o _ e v e n t _ c a l l _ a l l _ f u n c (ip, GPIO_EVENT_FUNC_SUSPEND); / / 调 用 su sp e n d
ip->info->power(ip->info, 0);

v o i d g p i o _ e v e n t _ r e s u m e ( s t r u c t e a r l y _ s u s p e n d *h) {
struct gpio_even七*ip;
ip = container_of(h, struct gpio_event, early_suspend);
ip->info->power(ip->info, l);
g p i o _ e v e n t _ c a l l _ a l l _ f u n c (ip, GPIO_EVENT_FUNC_RESUME); . / / 调 用 re sum e

----- ----
在驱动的probe过程中,注册了early_suspend, 如下所示:
ilifdef C O N F I G HAS E A R L Y S U S P E N D
ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_.SCREEN + l;
i p - > e a r l y _ s u s p e n d . s u s p e n d = gpio_event_suspend; // e a r l y _ s u s p e n d
i p - > e a r l y _ s u s p e n d . r e s u m e = gpio_event_resume; // late_resume
register_early_suspend(&ip->early_suspend);
fendif

设备的删除remove过程中,将注销early_suspend, 如下所示:
Sta七ic int g p i o _ e v e n t _ 工em ov e (s tr u ct p l a t f o r m _ d e v i c e *pdev)(
st 工u c t gpio_e v e n t *ip = p l a t f o r m _ g e t _ d 工v d a ta (pdev) ;
inti;
gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); //调用反注册
if (ip->info->power) {
h f d e f C O N F I G HAS E A R L Y S U S P E N D
unregister_early_suspend(&ip->early_suspend); II注销early_ uspend
fondif
平今士nfo 今 p ow e r (ip 一江 n fo , 0);

for (i = O; i < ip->input_devs->count; i++)


input_unregister_device(ip->input_devs->dev[i));
kfree(ip);
return O;

GPIO的驱动程序本身是 一 个公用的驱动,而实际上有哪些设备 (引脚)则是通过板级

395
•oo Android板级支持与硬件相关子系统

定义的gpio _ event_platform _ data的数据定义来确定的,因此gpio_event驱动中对挂起和恢


复的调用需要对所有的设备进行。gpio _ event_ cal I_all_ func()函数包含 一 个循环 ,会调用所
有GPIO设备的Suspend或者resU1ne。

26.4.2 用户空间的控制
Android系统本身提供了内核中的wakelock和earlysyspend机制,以及在用户空间控
制wakelock的接口。使用Android系统的设备,具有自身的特点。从电源管理的角度,除
了这些机制之外,还需要使用Linux系统 一 些 已有 的机制 。几个与硬件相关子 系统各 自有
特殊的处理情况。
C l ) 关 于 W i F i 和 B l u e Tooth对rfkill的使用
Linux的r加ll原本也是电源管理的额外机制,通过为用户空间提供sys文件系统的节
点,来对设备进行控制。对于W正1和Blue Tooth这种耗能较高的设备,通常需要使用r加11
进行主动开启和关闭。Android系统也使用了rfkill机制,并在W中和BlueTooth中的用户
空间封装了功能,一直到Java层均可以进行调用。此种主动调用的电源管理机制与系统挂
起恢复的流程无关。
(2)电话
Android电话的RIL HAL需要处理电话部分的功耗。尤其是在系统进入了Idle状态之
后,还必须保留来电和短信的接收功能。除此之外,飞行模式要进行特殊处理。
(3) Event设备
Android系统的用户输入系统的EventHub部分使用了对Event驱动的遍历处理。但是
在实际应用的场景中,不仅用户输入设备使用Event驱动,传感器、蓝牙也可能构建Event
设备驱动。它们也受到EventHub的管理,都会阻止系统进入低功耗的模式。
(4) G P S 的 主 动 开 关
GPS的开关和定位系统应用层比较相关,它不属于主要的功能,但通常消耗较大的能
源。Android的GPS使用的是驱动中直接包括电源管理特性的方式,也属于主动调用的。
实际上,在Android系统Java层使用android.os包中的PowerManager类及其嵌套类
WakeLock可以进行电源管理的控制,WakeLock类型与底层的定义略微不同,考虑了屏幕
和键盘的情况。
PowerManager. WakeLock的参数中几个标志的含义如下所示。
a PARTIAL_ W A K E _ L O C K (Oxl): CPU不会停止运转,屏幕和键盘可以关闭。
a S C R E E N _ D I M _ W A K E _ L O C K ( O x a ) 和 S C R E E N _ B R I G H T — W A K E _ L O C K (Ox6):
CPU不会停止运转,屏幕保持变暗或者开,键盘可以关闭。
a F U L L _ W A K E _ L O C K (Oxla): CPU不会停止运转,屏幕和键盘都保持开启。
一个最基本的原则就是:无论哪种WakeLock被持有,CPU都不会停转。而由于屏幕
在Android系统中比较特殊,则可以单独进行控制。所有的调用最终都是通过sys文件系统
操作驱动程序,屏幕是否能实现这种常开和变暗的特性,则又和底层的实现相关。
从Android系统而言,Java应用程序也可以通过对Power Manager调用,对系统的电源
管理有较多的干涉,这与标准的Linux系统有不同的理念。

�396
第27章
恢复和升级

27.1 恢复和升级概述

恢复(Recovery)和升级(Update)是系统改变自身软件的手段:恢复通常是指将系
统的软件还原到某个出厂的版本;升级是指将系统的软件改变成某个比较新的版本,其中
可能包括增量的升级。
Android系统的恢复和升级在软件上使用了相似的结构和流程,并且在实现上统称为
Recovery。恢复机制考虑了Android系统自身的特点和用户可能的需求。

汕 27 .1.1 Android 的 Recovery 系统 的组 成



个正常运行 的 A ndroid 系统 由几个不 同的映像组成 :引导加载器(B ootLoader)、Linux
内核、根文件系统(只读)、System分区(通常为只读)、Data分区(读写)、Cache分区(读
写)。其中Linux内核与根文件系统可能做在 一 个映像 中。
Android的Recovery系统的含义是正常的Android重新启动之后所运行的软件系统。
这个系统由引导加载器(Boot Loader)、Linux内核与根文件系统组成。
Recovery系统也分为类似的几个部分,各自具有特点。
o引导加载器:通常与正常运行的引导加载器相同,但是在启动的过程中根据不同的
参数,可以选择进入正常运行模式或者Recovery模式。
ft Linux内核:通常是 一 个独立 的内核 ,可 以和正常运 行的 L inux 内核相 同,或者是其
经过裁剪后的内容。
• Recovery根文件系统:提供了Linux系统用户空间的支持,包括与正常运行的Android
类似的init进程、init.rc脚本,以及主要的Recovery可执行程序。
综上所述,Android的Recovery系统的本质是 一 个小型的 L inux 系统 。其 引导加载器
和Linux内核均和正常运行的Android类似或者相同,不同的Android系统的Recovery差
异的部分主要体现在用户空间的Recovery根文件系统中。通常情况下,Recovery系统和正
常系统通用内核,而Recovery的Linux内核与根文件系统属于Recovery分区。因此Recovery
• O O Android板级支持与硬件相关子系统

功能只在目标系统中增加 一 个分区。

今 提 示 : Android的Recovery的软件实现相比其他系统复杂,可提供更多功能。

Recovery的大部分逻辑其实都包含在了用户空间中,这样的设计简化了Recovery程序
的编写,淡化了各个系统的硬件差异,并且可以让不同的Android系统进行差异化的定制。
由此,Android的Recovery系统虽然必然与硬件相关,但是Recovery相关的开发却集中在
与硬件无关的方面。

汕 27.1.2 Android的Recovery系统的功能和运行流程
正常运行的Android系统和Recovery系统都是Linux系统,它们都被Boot Loader启动,
究竟启动哪 一 个 ,则 由启动过程 中的参数决定 。
Android的正常运行和恢复模式如图27-1所示。

Boott.oi如

圃制嗣阑谭 恢复模式启动

图27-1 Android的正常运行和恢复模式

有些Android系统还具有 一 个特殊 的小型 M isc 分区 ,其 中可 以设置启动参数 ,由 B oot


Loader读取,可选择进入哪种模式。在开发阶段,通常可以通过Boot Loader的命令行界面
进入设置不同的启动参数。
Recovery运行之后,主要的逻辑在用户空间中,具有以下功能:
o提供简单的界面与用户进行交互。
o可以擦除和更新System、Data、Cache等分区。
o可以从Cache中读取升级包。
o具有差异比较算法,根据差异进行二进制的增量升级。
Recovery的文件系统功能只需要识别System和Data分区,以此进行擦除和更新,而
不需要运行其中的程序。Cache分区在正常运行中基本不需要被使用,它可以用于放置升
级包或者恢复包,也用千设置Recovery的参数。
Android从正常运行到Recovery的流程为:

398
第27章恢复和升级 oo•

o 将升级包放置在 C a c h e 分 区 (如果只需要擦 除 ,则不需要这个过程 )



o 正常运 行 中设置 C a c h e 分 区中的参数 (擦除 、升级等命令 )

o设置标志,并重新启动。
e Boot Loader 根据参数运行 R e c o v er y 系统 。
e Recovery 系统运行 (根据 命令运行参数或者与用户 的交互 )。
e Recovery 运行完 成后 ,设置参数 ,重启启动 。

27.2 r e c o v e r y 系统

Recovery 的文件 系统只是一 个根文件 系统 ,其 中通常包括 以下 内容 。


4t i n i t 可执行程序和 i n i t .r c 脚本 :用于拟行初始化动作 。
4t / d e v / 、/ et c / 、/ p r o c / 等 :L i n u x 系统运行必要 的 目录 。
4t r e c o v e r y 可执行程序 :用于执行 R e c o v e r y 的主要逻辑 。
a res/images: 包括各种资源图片,用于展示UI界面。

�27.2.1 编译系统
在 A n d r o i d 的源代码环境 中,可 以编译生成 R ec o v er y 系统 的映像文件 ,当可 以编译 时 ,
执行 m ak e r e c o v e r y i m a g e 可 以生成 r ec o v e r y.i m g 。r ec o v er y .i m g 本身 由 R e c o v e r y 的 L i n u x 内
核和放置根文件系统的 R a m D i sk 组成 ( r am d i sk - r ec o v er y .i m g ) 。
在 A n dro id 系统编译 中,主编译文件 c o r e/ M ak e f i l e 中包含 了生成 r ec o v e r y .i m g 的核 心
逻辑,如下所示:
$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) $ ( M I N I G Z I P ) \
$(INSTALLED_RAMDISK_TARGET) $(INSTALLED_BOOTIMAGE_TARGET) \
$(recovery_binary) $(recovery_initrc) $(recovery_kernel) \
$(INSTALLED_2NDBOOT LOADER_TARGET) $ ( r e c o v e r y _ b u i l d _ p r o p ) \
$(recovery_resource_deps) $(recovery_fstab) \
$(RECOVERY INSTALL OTA KEYS)
@ e c h o - - - - - Making r e c o v e r y image 一 一 一
r m - r f $(TARGET_RECOVERY_OUT)
m k d i r - p $(TARGET_RECOVERY_OUT)
m k d i r - p $(TARGET_RECOVERY_ROOT_OUT)
m k d i r - p $(TARGET_RECOVERY_ROOT_OUT)/etc
m k d i r - p $(TARGET_RECOVERY_ROOT_OUT)/tmp
echo Copying b a s e l i n e ramdisk • • .
c p - R $(TARGET ROOT OUT) $(TARGET RECOVERY OUT)
rm $(TARGET_RECOVERY_ROOT_OUT)/in扛*.rc
echo M o d i f y i n g ramdisk c o n t e n t s . • •
c p - f $ ( r e c o v e r y _ i n i 七 r c ) $(TARGET_RECOVERY_ROOT_OUT)/
c p - f $ ( r e c o v e r y _ b i n a r y ) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
c p - r f $ ( r e c o v e r y _ r e s o u r c e s _ c o m m o n ) $(TARGET_RECOVERY_ROOT_OUT)/
$(foreach item,$(recovery_resources_private), \
c p - r f $ ( i t e m ) $(TARGET_RECOVERY_ROOT_OUT)/)
$(foreach让em,$(recovery_fstab), \
c p - f $ ( i t e m ) $(TARGET_RECOVERY_ROOT_OUT)/etc/recovery.fstab)
c p $(RECOVERY_INSTALL_OTA_KEYS) $(TARGET_RECOVERY_ROOT_OUT)/res/keys
c a t $(INSTALLED_DEFAULT_PROP_TARGET) $ ( r e c o v e r y _ b u i l d _ p r o p ) \
> $ (TARGET_RECOVERY_ROOT_OUT) / d e f a u l t . p r o p

399
•oo Android板级支持与硬件相关子系统

$(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) I $(MINIGZIP) > $(recovery_ramdisk)


$(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) - - o u t p u t $ @
@ e c h o - - - - - Made recovery i m a g e - - - - - - - - $ @
$(hide) $(call assert-max-image-size,$@, \
$ (BOARD RECOVERYIMAGE PARTITION SIZE), raw)

INSTALLED_RECOVERYIMAGE _T A R G E T 定 义 的 是 R e c o v e r y 内 核 与 根 文 件 系 统 的 合
成映像。想要生成这个目标,需要依赖内核、Recovery可执行程序、init.rc等内容,还需
要依赖于主机的mkimage、zip等工具。
生成的过程分为以下几个步骤:
o 首 先 生 成 根 文 件 系 统 目 录 $ ( T A R G E T _ R E C O V E R Y _ OUT)中的各个文件。
e $(MKBOOTFS)用于生成根文件系统映像(ramdisk-recovery.img)。

e $(M邸OOTIMG)将内核映像和根文件系统映像连接在 起 , 生 成 reco v ery.im g 。
生成recovery.img的基本条件是必须要有Linux内核,因此板级的编译变量
TARGET_NO_KERNEL不能为true, 因此仿真器是没有Recovery的。

�27.2.2 init.rc脚本
Recovery系统中的init可执行程序和正常运行的Android系统基本相同,但是init.rc

不同。Recovery系统的init也是用户空间运行的第 个 进 程 ,依 靠 特 殊 的 in it.rc , Recovery
系统可进入与正常不同的流程。
一个默认的Recovery的m止re脚本如下所示:
on init
export PATH /sbin
export ANDROID_ROOT /system
export ANDROID_DATA /data
expor七EXTERNAL_STORAGE /sdcard
symlink /sys七em/etc /etc
mkdir /sdcard
mkdir /sys七em
mkdir /data
mkdir /cache
mount /tmp /tmp tmpfs
on boot
ifup lo
hostname localhost
domainname localdomain
class start default
service recovery /sbin/recovery
service adbd /sbin/adbd recovery
disabled
on property:persist.service.adb.enable=l
start adbd
on property:persist.service.adb.enable=O
stop adbd

此处的init.rc比正常运行的init.rc要简单很多,除了on init和on b o o t 阶 段 的 固 定 流 程
之外。主要的内容就是启动Recovery可执行程序,作为同名的服务运行,这是Recovery
系统的主要运行程序。init中也启动了adbd, 为了可以在主机使用adb连接到目标系统。

�400
第27章恢复和升级 ooe

�27.2.3 Recovery可执行程序和相关的库
Recovery工程的代码在bootable/recovery目录中,各个子目录中的内容如下所示。
o 根 目 录 : Recovery可执行程序。
minui/: 用 于 提 供 简 单 的 U T , 生成lib血nui.a静态库。
e minzip/: 用 于 处 理 Z i p 压 缩 包 , 生 成 l i b m i n z i p . a 静 态 库 。
e mtdutils/: 用 千 处 理 M T D 的 分 区 , 生 成 l i b m t d u t i l s . a 静 态 库 。
e applypatch/: 生成libapplypatch.a静态库。
e edify/: 用 于 处 理 正 则 表 达 式 , l i b e d i f y . a 静 态 库 和 运 行 千 主 机 的 e d i f y 可 执 行 程 序 。
e updater/: 用 千 处 理 升 级 和 安 装 的 程 序 , 生 成 U p d a t e r 可 执 行 程 序 。
e tools/: 生成add-property-tag可执行程序。
此处生成的各个静态库,都只被此处的可执行程序使用。由于Recovery系统很小,因
此recovery可执行程序中使用的各种功能都需要自己实现,如果链接外部的库,将会大大
增加程序的尺寸。

1. Recovery可执行程序

Recovery包括recovery.c、BootLoader.c、install.c、roots.c和ui.c等文件,并且链接了
lib皿nui.a、lib血nzip.a、libmtdutils.a等静态库。目录也生成了verifier_t e s t 可 执 行 程 序 , 用
于测试程序。
recovery.c中首先具有如下的定义:
static canst struct option OPTIONS[) = { // Android传入的命令
{ "send_intent", required_argument, N U L L , ' s ' } , //向Android设置Intent
{ "update_package", required_argument, N U L L , ' u ' } , //升级包
{ "wipe_data", no_argument, N U L L , ' w ' } , //清除data分区
{ "wipe_cache", no_argument, N U L L , ' c ' } , //清除cache分区
{ "set_encrypted一丘lesystems", required_argument, NULL, ,e, },
{ "show_text", no_argument, N U L L , ' t ' ) ,
{ NULL, 0, NULL, 0 },
);
static const char *COMMAND_FILE = "/cache/recovery/command"; // Android系统传入的
static const char *INTENT_FILE = "/cache/recovery/intent"; // Recovery传出的
static const char *LOG FILE"" "/cache/recovery/_log"; 一 // Rec::overy传出的

Recovery系统和Android正常运行的交互依靠Cache文件系统,Android系统传向
Recovery的命令放入command文件中,而Recovery则会写intent和log两个文件,向正常
启动传回参数。option类型的数组就是Android传入的Recovery的命令。
Recovery可执行程序的main()函数的主运行流程如下所示:
int main(int argc, char **argv) {
t i m e 七 s t a r 七 = time(NULL);
//省略: h 开 s七dout 、s七de rr
ui_init(); // minui的初始化
ui_set_background(BACKGROUND_ICON_INSTALLING);
load_volume_table ();
ge七_args (&argc, &argv); //从Cache文件系统获取命令行的参数
int previous_runs = 0;

401�
•oo Android板级支持与硬件相关子系统

c o n s t c h a r * s e n d _ i n t e n t = NULL;
c o n s t c h a r *upda七e_package = NULL; c o n s t c h a r * e n c r y p t e d _ f s _ m o d e = NULL;
i n t w i p e _ d a t a = 0, w i p e c a c h e = O;
i n t t o g g l e _ s e c u r e _ f s = 0; encrypted f s i n f o encrypted f s data;
i n t arg;
while ((arg = getopt_long(argc, a r g v , " " , OPTIONS, NULL)) ! = - 1 ) { / / 参 数 解 析
switch { a r g ) { / / i n t 类 型 的 a r g 是 一 个 表 示 命 令 的 单一 字 符
c a s e ' p ' : previous_runs = a t o i ( o p t a r g ) ; break;
case's': send_intent = optarg; break;
case'u': update_package = o p t a r g ; break;
c a s e ' w ' : wipe_da七a= wipe_cache = l ; break;
case'c': wipe_cache = l ; b r e a k ;
case'e': encrypted_fs_mode = optarg; toggle_secure_fs = l ; break;
case't': u i show t e x t ( l ) ; b r e a k ;
case'?':
L O G E ( " I n v a l i d command a r g u m e n t \ n " ) ;
continue;

device_recovery_start(); II调用UI相关的函数
if (update_package) { //对应更新的处理
if ( s t r n c m p ( u p d a t e p a c k a g e , "CACHE:", 6) == 0) { / / 处 理 C a c h e 中 的 内 容
i n t l e n = strlen(update_package) + 10;
char* modified_path = malloc(len);
strlcpy(modified_path, "/cache/", len);
S七rlcat(modified_path, update_package+6, len);
update卫ackage = modified_path; //设定包的路径

property_list(print_proper七y, NULL); //获得属性的列表


i n t s t a t u s = INSTALL_SUCCESS; //到此处表示前面已经执行成功,后面是命令处理
i f (toggle_secure_fs) {
// 省 略 : 安 全 文 件 系 统
} e l s e i f ( u p d a t e _ p a c k a g e ! = NULL) { //进行升级包的操作
sta七us= install_package(update_package);
if (sta七us ! = INSTALL_SUCCESS) u i _ p r i n t ( " I n s t a l l a t i o n a b o r t e d . \ n " ) ;
} else if (wipe_data) { //清除da七a分区的数据
i f ( d e v i c e _ w i p e _ d a t a ( ) ) sta七us = INSTALL_ERROR;
if {erase_volume("/data")) s七atus = INSTALL_ERROR;
i f ( w i p e _ c a c h e && e r a s e _ v o l u m e ( " / c a c h e " ) ) s t a t u s = INSTALL_ERROR;
i f ( s t a t u s ! = INSTALL_SUCCESS) u i _ p r i n t ( " D a t a w i p e f a i l e d . \ n " ) ;
} else if (wipe_cache) { //清除Cache分区的数据
i f ( w i p e _ c a c h e && e r a s e _ v o l u m e ( " / c a c h e " ) ) s t a t u s = INSTALL_ERROR;
i f (sta七us ! = INSTALL_SUCCESS) u i _ p r i n t ( " C a c h e w i p e f a i l e d . \ n " ) ;
) e l s e { s七atus = INSTALL_ERROR; } //没有命令的处理
if ( s t a t u s ! = INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
i f (s七atus ! = INSTALL_SUCCESS 11 u i _ t e x t _ v i s i b l e ( ) ) {
prompt and w a i t ( ) ; //在UI上给用户提示

finish_recovery(send_intent); //准备重新启动系统
ui_print("Rebooting ... \n");
sync() ;
reboot(RB_AUTOBOOT); //重新启动系统
r e t u r n EXIT_SUCCESS;

Recovery可执行程序的运行从获取命令参数开始,get_arg O函数获得参数实际上不是
来自命令行,而是来自/cache/recovery/command文件,也就是Android系统正常运行时留下

402
第27章恢复和升级 ooe

的;随后将进行系统对参数的解析,取得后面 要执行的内容;将通过 if else 进行各种升级



包、擦除数据的处理; 切都完成之后 ,进入重新启动 的阶段 。在 R e c o v er y 执行 的过程 中 ,
调用 UI 相关 的函数 向用户进 行显示 。
在各种命令的执行中,最复杂的是包的升级。 i n st a l l_ p ac k ag e O 函数在 i n st a l l .c 文件 中实

现,此时升级的包是 个包括 了所有 目标文件系统映像 的 O T A (Over The Air, 指空中下载
的)包。在 A n dro id 系统 中,这个包通常就是 O T A .z i p , 可以被放置在 SD 卡 、C ac h e 分区
或者其他用户自定义的目录中。
OTA 包升级 的过程分为 以下几个步骤 。

o 第 步 :根据路径得到 O T A 包 的文件 。
.第二步:通过公钥验证文件。
o 第三步 :通过 Z i p 库解压缩包 。
.第四步:执行 一 个可执行程序 (如 U p d at er ) 将包更新到系统 的分区中。

提示:更新程序位于 OTA 包中 ,R e c o v er y 从其 中解析 出来运行 。

2. Recovery UI 部分
Recovery 的 U I 是一 个 与逻辑有关 ,而与具体显示无关 的实现 ,在系统 中是一 个可替
换的部分。在 R ec o v e r y 根 目录 的 A n d r o i d .mk 文件 中有如下定义 :
i f e q ( $ (TARGET RECOVERY U I L I B ) , )
LOCAL SRC F I L E S + = d e f a u l t r e c o v e r y u i . c
else
LOCAL STATIC L I B R A R I E S + = $(TARGET RECOVERY U I L I B )
endif

TARGET_ RECOVERY_ UI_LIB 变量用于控制 R ec o v er y 的 U I 实现 :如果这个变量没有


定义,使用 d e f au l t_ r ec o v er y_ u i . c 文件作为 U I 的实现 ;如果这个变量在板级实现 中定 义了,
则使用定义的文件作为实现。
Recovery 的 U I 实现 的函数均在 r e c o v e r y_ u i . h 文件 中定义 ,如下所示 :
extern int device recovery start(); II r e c o v e r y 开始阶段 的初始化
//按键发生之后的显示,在输入线程中被调用
extern int device t o g g l e d i s p l a y ( v o l a t i l e char* key pressed, int key_code);
//通过按键系统重新启动,在输入线程中被调用
extern int d e v i c e r e b o o t 一n o w ( v o l a t i l e c h a r * k e y _ p r e s s e d , int key_code);
//等待一个输入按键,-在主线程 中被调用
e x t e r n i n t d e v i c e _ h a n d l e _ k e y ( i n t key, i n t v i s i b l e ) ;
extern i n t device__perform_action(int which); //执行 一 个菜单的项 目,参数 是索 引
i n t device wipe data() ; //擦除数据
e x t e r n c h a r * MENU_HEADERS[]; //定义菜单的头部
e x t e r n char*_MENU_ITEMS[]; //定义菜单的项目

recovery.c 使用 了 M
中的 p r o m p t_ a n d _w a 双) EN U _ H E A D E R S 和 M E N U _ IT E M S 进行 了
菜单的显示,又调用 d ev i c e_ p e r f o r m _a c t i o n O 执行 内容 。由此 ,菜单显示 的 内容和选 中后的

执行,均可以任意定制, R ec o v e r y 的核心部分则负责根据 内容进行显示和选择 。


d e f a u l t _ r e c o v e r y_ u i . c 作为默认实现 ,其 中定义 了重启 、从 SD 卡 中升级 、擦除 d at a 和

403�
• O O Android板级支持与硬件相关子系统

擦除Cache几个菜单项目。

3. minui部分
minui用于为Recovery提供简单的UI, 包括了显示部分、用户的输入、PNG图片的处
理和字体等功能。minui目录中的内容包括graphics.c、events.c等几个源文件。
呻ui.h中以gr (含义为Graphic)为开头的函数与显示有关,如下所示:
int gr_init(void); //显示的初始化
void gr_exit(void}; I I 显示的退出
int gr_fb_width(void);
int gr_fb_height(void};
gr_pixel•gr_fb_data(void};
void gr_flip(void};
v o i d g r _ c o l o r ( u n s i g n e d c h a r r , unsigned c h a r g, unsigned c h a r b, unsigned char a ) ;
v o i d g r _ f i l l ( i n t x , i n t y , i n t w, i n t h ) ;
i n t g r t e x t ( i n t x, i n t y, canst char * s } ; I I 显示文本
i n t gr_measure(const char * s } ;
v o i d g r _ b l i t ( g r _ s u r f a c e s o u r c e , i n t s x , i n 七 s y , i n t w, i n t h , i n t d x , i n t dy};
unsigned i n t gr_get_width(gr_surface surface};
unsigned i n t gr_get_height(gr_surface surface};

gr_ini叨函数将打开首个终端设备C/dev/ttyO)和Framebuffer设备C /dev/graphics/fbO),
其他的各个函数都通过向设备中输出来完成显示内容。
minui.h中以ev (含义为Event)为开头的函数与显示有关,如下所示:
s t r u c t inpu七_event; //来自于 L i nu x 标准头文件 i np u t . h 的定义
int ev_init(void);
void ev_exit(void);
i n t e v g e t ( s t r u c t i n p u t e v e n t * e v , u n s i g n e d d e n t wait)一;

在ev_ini叨函数中查看/dev/input目录中的Event设备文件,并从其中进行获取用户输
入的事件,ev_get()函数则通过对Event设备进行poll()调用,获取其中的内容。

今提示:皿nui库提供了相当单纯的功能,ui.c中建立了两个线程调用其接口函数。

4. minzip部分
血皿ip实现了简单的Zip算法,包括了Zip.c、Hash.c、SysUtil.c、D订Util.c等几个源代
码文件,这里也引用了Zip库的头文件,但是没有链接任何库。
头文件zip.h中的几个函数如下所示:
i n t mzOpenZipArchive(const char* fileName, ZipArchive * p A r c h i v e ) ;
v o i d mzCloseZipArchive(ZipArchive* pArchive);
c o n s t Z i p E n t r y * m z F i n d Z i p E n t r y ( c o n s t ZipArch�ve* p A r c h i v e , c o n s t c h a r * entryName);

这些以mz为开头的函数代表的就是皿皿ip的含义。以上三个函数分别用于打开Zip
文件、关闭Zip文件和查找Zip的入口,一个Zip文件打开之后的操作均以ZipArchive为
句柄。

}提示: minzip库被升级部分所调用,用于解析OTA.zip包。

404
•oo Android板级支持与硬件相关子系统

public static v o i d verifyPackage ( F i l e packageFile,


RecoverySystem.ProgressListener l i s t e n e r , File deviceCertsZipFile)

用于安装包的installPackage()函数的实现如下所示:
p u b l i c s t a t i c v o i d installPackage(Context context, F i l e packageFile)
throws IOException {
S t r i n g f i l e n a m e = p a c k a g e F i l e . g e t C a n o n i c a l P a t h () ;
S七ring a r g = " - - u p d a t e _ p a c k a g e= " + f i l e n a m e ;
bootCommand ( c o n t e x t , a r g ) ; //设置启动命令

用于重启清除数据的rebootWipeUserData ()函数的实现如下所示:
p u b l i c s 七 a t i c v o i d rebootWipeUserData(Context context)七hrows IOException {
f i n a l C o n d i 已 o n V a r i a b l e c o n d i t i o n = new C o n d i t i o n V a r i a b l e () ;
II 省略:发送 " a n d r o i d . i n t e n t . a c t i o n . MASTER CLEAR N O T I F I C A T I O N " 广播
condition.block();
bootCommand(con七ext, " - - w i p e d a t a " ) · , //设置启动命令

实际上各种命令的执行都是通过向Cache文件系统中写入命令来完成,核心实现部分
如下所示:
priva七e s t a t i c F i l e RECOVERY_DIR = new F i l e ( " / c a c h e / r e c o v e r y " ) ;
priva七e s t a t i c F i l e COMMAND_FILE = new F i l e ( R E C O V E R Y _ D I R , " c o m m a n d " ) ;
private s t a t i c F i l e LOG_FILE = new F i l e ( R E C O V E R Y _ D I R , " l o g " ) ;
p豆vate s t a t i c S t r i n g LAST_LOG_FILENAME = " l a s t _ l o g " ;
private s t a t i c v o i d bootCommand(Context c o n t e x t , S t r i n g a r g )
throws IOException {
RECOVERY_DIR.mkdirs ( ) ; / / 如 果 没 有 r ec ov e r y , 目录则建立它
COMMAND F I L E . d e l e t e ( ) ; //删除其中的 c omma n d 文件
LOG_FILE.delete ( ) ;
F i l e W r i t e r c o m m a n d = new F i l e W r i t e r ( C O M M A N D _ F 工 L E ) ; / / 准 备 写 入 c omma n d 文件
try {
command.write(arg); / / 根 据 参 数 向 c omman d 文件 中写入 内容
command.write("\n");
) finally { command.close();) //关闭 c omma n d 文件
P o w e r M a n a g e r pm
= (PowerManager) c o n t e x t . g e t S y s t e m S e r v i c e ( C o n t e x t . P O W E R _ S E R V I C E ) ;
pm. r e b o o t ( " r e c o v e r y " ) ; / / 重 新 启 动 到 Re c ov e r y 模式
t h r o w new I O E x c e p t i o n ( " R e b o o t f a i l e d ( n o p e r m i s s i o n s ? ) " ) ; // 一 般不会进入

bootCommand()函数的实现相当简单,就是向/cache/recovery/目录的command文件写
入命令,这些命令就是要Recovery系统执行的内容。

�27.3.2 交互的场景
Android正常运行系统和Recovery系统通过Cache分区进行交互,使用的就是
/cache/recovery/目录中的儿个文件,如下所示。
• /cache/recovery/command: 由Android写入,Recovery读出执行的命令。
• /cache/recovery/intent: 由Recovery写入,Android读出执行的Intent。
• /cache/recovery/log: Recovery执行过程中写入的Log。
Android会写入command文件的命令,主要具有以下格式。

406
第27章恢复和升级 ooe

e --send_intent=anystring: 向intent文件写入anystring -个字符串。


e --update_package=path: 以path作为升级包的路径。
e --wipe_data: 清除Data分区。
e --wipe_cache: 清除Cache分区。
e --set_encrypted_filesystem = onloff: 设置是否使用加密的文件系统。
send_intent命令比较特殊,后面的字串Recovery系统并不认识,但是要写入到Intent
中,实际上就是Android系统传给自己的信息,为了在第二次重启之后能够执行 一 些不同
的东西。当Recovery系统成功执行之后,还会清除command文件。
Android对Recovery的使用主要有恢复出厂设置(FACTORY RESET)、升级COTA
INSTALL)和加密文件系统(ENCRYPTED FILE S Y S T E M S E N A B L E / D I S A B L E ) 等
例如,如果执行了Settings = > Privacy = > Factory Data Reset, 在系统还没有重新启动之
前,可以在命令行终端下看到如下信息:
jl cat /cache/recovery/command
--wipe_data

按照这个命令,Recovery系统运行之后,则会清除Data分区和Cache分区,然后重新
启动。
如果执行了升级操作,则需要在command文件中写入升级命令,其格式类似
"--update_package= /cache/som e-filenam e.zip", 后面的路径就表示要升级的OTA文件。
Recovery的运行将会比较复杂,要经历解析包和安装二进制映像的过程,由于执行时间比
较长,还会提示用户等待,等待完成之后,会清除Cache分区。

407�

You might also like