一、概述
本文希望通过分析台湾的Jollen的mokoid 工程代码,和在s5pc100平台上实现过程种遇到的问题,解析Andorid HAL的开发方法。
二、HAL介绍
现有HAL架构由Patrick Brady (Google) 在2008 Google I/O演讲中提出的,如下图。
Android的HAL是为了保护一些硬件提供商的知识产权而提出的,是为了避开linux的GPL束缚。思路是把控制硬件的动作都放到了Android HAL中,而linux driver仅仅完成一些简单的数据交互作用,甚至把硬件寄存器空间直接映射到user space。而Android是基于Aparch的license,因此硬件厂商可以只提供二进制代码,所以说Android只是一个开放的平台,并不是一个开源的平台。也许也正是因为Android不遵从GPL,所以Greg Kroah-Hartman才在2.6.33内核将Andorid驱动从linux中删除。GPL和硬件厂商目前还是有着无法弥合的裂痕。Android想要把这个问题处理好也是不容易的。
总结下来,Android HAL存在的原因主要有:
1. 并不是所有的硬件设备都有标准的linux kernel的接口
2. KERNEL DRIVER涉及到GPL的版权。某些设备制造商并不原因公开硬件驱动,所以才去用HAL方 式绕过GPL。
3. 针对某些硬件,An有一些特殊的需求。
三、HAL内容
1、HAL 主要的储存于以下目录:
(注意:HAL在其它目录下也可以正常编译)
● libhardware_legacy/ - 旧的架构、采取链接库模块的观念进行
● libhardware/ - 新架构、调整为 HAL stub 的观念
● ril/ - Radio Interface Layer
● msm7k QUAL平台相关
主要包含以下一些模块:Gps、Vibrator、Wifi、Copybit、Audio、Camera、Lights、Ril、Overlay等。
2、两种 HAL 架构比较
目前存在两种HAL架构,位于libhardware_legacy目录下的“旧HAL架构”和位于libhardware目录下的“新HAL架构”。两种框架如下图所示。
![]() | ![]() |
图3.1 旧HAL架构 图3.2 新HAL架构
libhardware_legacy 是将 *.so 文件当作shared library来使用,在runtime(JNI 部份)以 direct function call 使用 HAL module。通过直接函数调用的方式,来操作驱动程序。当然,应用程序也可以不需要通过 JNI 的方式进行,直接加载 *.so (dlopen)的做法调用*.so 里的符号(symbol)也是一种方式。总而言之是没有经过封装,上层可以直接操作硬件。
现在的 libhardware 架构,就有stub的味道了。HAL stub 是一种代理人(proxy)的概念,stub 虽然仍是以 *.so檔的形式存在,但HAL已经将 *.so 档隐藏起来了。Stub 向 HAL提供操作函数(operations),而 runtime 则是向 HAL 取得特定模块(stub)的 operations,再 callback 这些操作函数。这种以 indirect function call 的架构,让HAL stub 变成是一种包含关系,即 HAL 里包含了许许多多的 stub(代理人)。Runtime 只要说明类型,即 module ID,就可以取得操作函数。对于目前的HAL,可以认为Android定义了HAL层结构框架,通过几个接口访问硬件从而统一了调用方式。
下面结合实例来分析HAL编程方法。
四、mokoid 工程代码下载与结构分析
1、mokid项目概述
modkoid工程提供了一个LedTest示例程序,是台湾的Jollen用于培训的。对于理解android层次结构、Hal编程方法都非常有意义。
2、下载方法
#svn checkout http://mokoid.googlecode.com/svn/trunk/mokoid-read-only
3、结构分析
![]() ![]() ![]() |
Android的HAL的实现需要通过JNI(Java Native Interface),JNI简单来说就是java程序可以调用C/C++写的动态链接库,这样的话,HAL可以使用C/C++语言编写,效率更高。在Android下访问HAL大致有以下两种方式:
(1)Android的app可以直接通过service调用.so格式的jni
(2)经过Manager调用service
上面两种方法应该说是各有优缺点,第一种方法简单高效,但不正规。第二种方法实现起来比较复杂,但更符合目前的Android框架。第二种方法中,LegManager和LedService(java)在两个进程中,需要通过进程通讯的方式来通讯。
mokoid工程中实现了上述两种方法。下面将详细介绍这两种方法的实现原理。
4、第一种方法:直接调用service方法的实现过程
下面分析第一种方法中,各层的关键代码。
(1)HAL层
一般来说HAL moudle需要涉及的是三个关键结构体:
struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;
下面结合代码说明这3个结构的用法。部分代码经过修改,后面的章节会给出修改的原因。
文件:mokoid-read-only/hardware/modules/include/mokoid/led.h
/***************************************************************************/
struct led_module_t {
struct hw_module_t common;
};
//HAL 规定不能直接使用hw_module_t结构,因此需要做这么一个继承。
struct led_control_device_t {
//自定义的一个针对Led控制的结构,包含hw_device_t和支持的API操作
struct hw_device_t common;
/* attributes */
int fd; //可用于具体的设备描述符
/* supporting control APIs go here */
int (*set_on)(struct led_control_device_t *dev, int32_t led);
int (*set_off)(struct led_control_device_t *dev, int32_t led);
};
#define LED_HARDWARE_MODULE_ID "led"
//定义一个MODULE_ID,HAL层可以根据这个ID找到我们这个HAL stub
文件:mokoid-read-only/hardware/modules/led/led.c
![]() ![]() ![]() |
(2)JNI层
文件:mokoid-read-only/frameworks/base/service/jni/com_mokoid_server_LedService.cpp
![]() ![]() ![]() ![]() |
(3)service (属于Framework层)
(4)APP 测试程序 (属于APP层)
文件:apps/LedClient/src/com/mokoid/LedClient/LedClient.java
5、第二种方法:经过Manager调用service
HAL、JNI两层和第一种方法一样,所以后面只分析其他的层次。
(1)Manager (属于Framework层)
APP通过这个Manager和service通讯。
文件:mokoid-read-only /frameworks/base/core/java/mokoid/hardware/LedManager.java
![]() ![]() |
因为LedService和LedManager在不同的进程,所以要考虑到进程通讯的问题。Manager通过增加一个aidl文件来描述通讯接口
文件:mokoid-read-only/frameworks/base/core/java/mokoid/hardware/ILedService.aidl
(2)SystemServer (属于APP层)
文件:mokoid-read-only/apps/LedTest/src/com/mokoid/LedTest/LedSystemServer.java
(3)APP 测试程序(属于APP层)
文件:mokoid-read-only/apps/LedTest/src/com/mokoid/LedTest/LedTest.java
![]() ![]() |
五、实验中需要注意的问题
将下载后的源码放到你的android源码目录下,然后编译系统。本实验用的android版本为2.1。实验的过程中大致出现过以下几个问题:
1、目标系统中没有生成LedClient.apk或LedTest.apk应用程序
编译完成后,没有在目标系统的system/app/目录下找到LedClient.apk或LedTest应用程序。只有通过单独编译LedClient或LedTest才能在目标目录中生成。方法如下:
#mmm mokoid-read-only/apps/LedTest/
检查原因后发现mokoid-read-only/apps/LedTest/Android.mk
LOCAL_MODULES_TAGS :=user
而我们的s5pc100系统在配置时tapas时选择的是eng,所以没有装载到目标系统。
所以修改LedTest和LedClient的Android.mk
LOCAL_MODULES_TAGS :=user eng
再次编译即可自动装载到目标系统/system/app/目录下。
2、启动后没有图标,找不到应用程序
目标系统启动后找不到两个应用程序的图标。仔细阅读logcat输出的信息发现:
E/PackageManager( 2717): Package com.mokoid.LedClient requires unavailable shared library com.mokoid.server; failing!
原因是找不到 com.mokoid.server。检查mokoid-read-only/frameworks/base/Android.mk发现系统将LedManager和LedService编译成 mokoid.jar库文件。为了让应用程序可以访问到这个库,需要通过com.mokoid.server.xml 来设定其对应关系。解决方法:拷贝com.mokoid.server.xml到目标系统的system/etc/permissions/目录下。
此时两个应用的程序的图标都正常出现了。
3、提示找不到 JNI_OnLoad
按照以前的实验加入下列代码:
![]() ![]() |
4、需要针对你的目标平台修改HAL的Makefile
修改mokoid-read-only/hardware/modules/led/Android.mk
LOCAL_MODULE := led.default
5、在eclipse中编译不了LedSystemServer.java
原因是程序中要用到ServiceManager.addService,这需要系统权限。
解决方法可以把应用程序放入Android源码中编译,并确保以下两点:
(1)在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。
(2)修改Android 加入LOCAL_CERTIFICATE := platform.
当然:mokoid工程源码中已经做了这些。
sudo: unable to change to sudoers gid: Operation not permitted
sudo: setresuid() [0, 0, 0] -> [121, -1, -1]: Operation not permitted
tar zxf VMwareTools-9.6.1-1378637.tar.gz
sourceryg++-2019.05-5-nios2-linux-gnu.src.tar.bz2
W: Duplicate sources.list entry http://ppa.launchpad.net/webupd8team/java/ubuntu/ precise/main amd64 Packages (/var/lib/apt/lists/ppa.launchpad.net_webupd8team_java_ubuntu_dists_precise_main_binary-amd64_Packages)
W: Duplicate sources.list entry http://ppa.launchpad.net/webupd8team/java/ubuntu/ precise/main i386 Packages (/var/lib/apt/lists/ppa.launchpad.net_webupd8team_java_ubuntu_dists_precise_main_binary-i386_Packages)
W: You may want to run apt-get update to correct these problems
$ sudo apt-cachesearch java | awk '{print($1)}' | grep -E -e '^(ia32-)?(sun|oracle)-java' -e'^openjdk-' -e '^icedtea' -e '^(default|gcj)-j(re|dk)' -e '^gcj-(.*)-j(re|dk)'-e 'java-common' | xargs sudo apt-get -y remove
$ sudo apt-get -yautoremove
W: Duplicate sources.list entry http://ppa.launchpad.net/webupd8team/java/ubuntu/ precise/main amd64 Packages (/var/lib/apt/lists/ppa.launchpad.net_webupd8team_java_ubuntu_dists_precise_main_binary-amd64_Packages)
W: Duplicate sources.list entry http://ppa.launchpad.net/webupd8team/java/ubuntu/ precise/main i386 Packages (/var/lib/apt/lists/ppa.launchpad.net_webupd8team_java_ubuntu_dists_precise_main_binary-i386_Packages)
W: You may want to run apt-get update to correct these problems
arm-none-linux-gnueabi-gcc -static -o first first.c
sudo ln -s /lib64/x86_64-linux-gnu/libc.so.6 /lib/libc.so.6
add-apt-repository "dev http://archive.canonical.com/lucid partner"
zhu@ubuntu:~$ cd /
zhu@ubuntu:/$ mkdir developer
mkdir: cannot create directory `developer': Permission denied
zhu@ubuntu:/$ su
Password:
root@ubuntu:/# mkdir developer
root@ubuntu:/# ls
bin dev home lib64 mnt root selinux tmp vmlinuz
boot developer initrd.img lost+found opt run srv usr
cdrom etc lib media proc sbin sys var
root@ubuntu:/# cd developer
root@ubuntu:/developer# mkdir jdk6
root@ubuntu:/developer# ls
jdk6
root@ubuntu:/developer# cd /mnt/hgfs/VMware/ls
bash: cd: /mnt/hgfs/VMware/ls: No such file or directory
root@ubuntu:/developer# cd /mnt/hgfs/VMware/
root@ubuntu:/mnt/hgfs/VMware# ls
123 jdk-6u45-linux-x64.7z
root@ubuntu:/mnt/hgfs/VMware# cp jdk* /developer/jdk6/
root@ubuntu:/mnt/hgfs/VMware# cd ^C
root@ubuntu:/mnt/hgfs/VMware# cd /developer/jdk6/
root@ubuntu:/developer/jdk6# ls
jdk-6u45-linux-x64.7z
root@ubuntu:/developer/jdk6# ./ jdk-6u45-linux-x64.bin
bash: ./: Is a directory
root@ubuntu:/developer/jdk6# ls
jdk-6u45-linux-x64.7z
root@ubuntu:/developer/jdk6# rm rf jdk-6u45-linux-x64.7z
rm: cannot remove `rf': No such file or directory
root@ubuntu:/developer/jdk6# rm -vf jdk-6u45-linux-x64.7z
root@ubuntu:/developer/jdk6# ls
root@ubuntu:/developer/jdk6# cd /
root@ubuntu:/# /mnt/hgfs/VMware/
bash: /mnt/hgfs/VMware/: Is a directory
root@ubuntu:/# cd /mnt/hgfs/VMware/
root@ubuntu:/mnt/hgfs/VMware# ls
123 jdk-6u45-linux-x64.bin
root@ubuntu:/mnt/hgfs/VMware# cp jdk* /developer/jdk6/
root@ubuntu:/mnt/hgfs/VMware# cd /developer/jdk6/
root@ubuntu:/developer/jdk6# ls
jdk-6u45-linux-x64.bin
root@ubuntu:/developer/jdk6# ./ jdk-6u45-linux-x64.bin
bash: ./: Is a directory
root@ubuntu:/developer/jdk6#
root@ubuntu:/developer/jdk6# developer/jdk6/
bash: developer/jdk6/: No such file or directory
root@ubuntu:/developer/jdk6# ./developer/jdk6/jdk-6u45-linux-x64.bin
bash: ./developer/jdk6/jdk-6u45-linux-x64.bin: No such file or directory
root@ubuntu:/developer/jdk6#
tar xvf jdk-6u45-linux-x64.7z -C /developer/jdk6/
7z x jdk-6u45-linux-x64.7z -r -o /developer/jdk6/
tar -xvf jdk-8u151-linux-x64.tar.gz -C /developer/jdk6
cp jdk-8u151-linux-x64.tar.gz /
/developer/jdk6/jdk1.8.0_151
export JAVA_HOME=/developer/jdk6/jdk1.8.0_151
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
zhu@ubuntu:~$ su
Password:
root@ubuntu:/home/zhu# cd /
root@ubuntu:/# source /etc/profile
root@ubuntu:/# echo $PATH
.:/developer/jdk6/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
root@ubuntu:/# java -version
The program 'java' can be found in the following packages:
* default-jre
* gcj-4.6-jre-headless
* openjdk-6-jre-headless
* gcj-4.5-jre-headless
* openjdk-7-jre-headless
Try: apt-get install <selected package>
root@ubuntu:/#
$sudo gedit /etc/profile
或者
$sudo -s
$gedit /etc/profile
这样打开profile文件,修改后就可以保存了。
让修改后的profile文件立即生效的方法:
法1:
$. /etc/profile
.和/etc/profile之间有一个空格
发2:
$source /etc/profile
source命令也称为“点命令”,也就是一个点符号(.)。source命令通常用于重新执行刚修改的初始化文件,使之立即生效,而不必注销并重新登录。
cp android-ndk-r8-linux-x86.tar.bz2 /developer/ndk8/
NDK=/developer/ndk8/android-ndk-r8
2.配置NDK的环境变量
执行:sudo gedit ~/.bashrc #在打开的文件末尾添加如下内容
NDK=/home/zhangz/develop/android-ndk-r8b
export NDK
3.在当前 bash 环境下读取并执行 ~/.bashrc 中的命令:
source ~/.bashrc
或 (source命令也称为“点命令”,也就是一个点符号(.)。)
. ~/.bashrc
4.然后查看是否生效:
执行:echo $NDK
结果:/home/zhangz/develop/android-ndk-r8b
5.编译有两种方法:
1)在目标工程目录下编译:
cd /home/zhangz/develop/workspace/HelloNDK/project #进入例子目录
$NDK/ndk-build #然后在当前目录下执行
2)在任意目录下编译:
$NDK/ndk-build -C /home/zhangz/develop/workspace/HelloNDK/project #用 -C 指出工程目录路径
注:无论哪种编译,成功后就会在当前目录下多生成 libs 与 obj 两个子目录。
/lib/x86_64-linux-gnu/libc.so.6
root@ubuntu:/developer/codesourcery/arm-2009q3/bin# arm-none-linux-gnueabi-gcc -v
arm-none-linux-gnueabi-gcc: command not found
root@ubuntu:/developer/codesourcery/arm-2009q3/bin# ./arm-none-linux-gnueabi-gcc -v
bash: ./arm-none-linux-gnueabi-gcc: No such file or directory
root@ubuntu:/developer/codesourcery/arm-2009q3/bin# cd /
root@ubuntu:/# #vim ~/.bashrc
root@ubuntu:/# vim ~/.bashrc
The program 'vim' can be found in the following packages:
将光盘中交叉编译工具的源码 arm-2009q3.tar.bz2
拷贝到/usr/local/arm(没有该
目录就新建一个)目录下,
并解压:
#tar jxvf arm-2009q3.tar.bz2
2)
修改环境变量:
#vim ~/.bashrc
在文件末添加
export PATH=/usr/local/arm/arm-2009q3/bin:$PATH
编译器安装成功。
#source ~/.bashrc
arm-none-linux-gnueabi-gcc -static -o first first.c
创建完main.c文件后,我们需要进行编写文件。
ubuntu下提供了一个程序编辑程序软件gedit。
我们借助命令gedit main.c就会弹出下图所示的编辑对话框,我们就可以在其中进行编写代码了,是不是很方便呢O(∩_∩)O
/usr/local/sdk/android-sdk-linux
export ANDROID_SDK_PATH=/usr/local/sdk/android-sdk-linux
sudo ln -s /usr/local/sdk/adt-bundle-linux-x86-20140702/sdk/platform-tools/adb /usr/bin/
sudo ln -s /usr/local/sdk/adt-bundle-linux-x86-20140702/sdk/tools/android /usr/bin/
sudo apt-get install lib32stdc++6
apt-get install git-core
git help git-checkout
mkdir -p /demo/helloworld-git
root@ubuntu:/demo/helloworld-git# cd /demo/helloworld-git
root@ubuntu:/demo/helloworld-git# echo "helloworld" > helloworld.txt
helloworld-master
root@ubuntu:/demo/helloworld-git# echo "helloworld" > helloworld.txt
root@ubuntu:/demo/helloworld-git# echo "helloworld" > helloworld.txt
root@ubuntu:/demo/helloworld-git# "shijienihao" > helloworld.txt
shijienihao: command not found
root@ubuntu:/demo/helloworld-git# echo "shijienihao" > helloworld.txt
root@ubuntu:/demo/helloworld-git# cat helloworld.txt
shijienihao
root@ubuntu:/demo/helloworld-git# git checkout helloworld.txt
root@ubuntu:/demo/helloworld-git# cat helloworld.txt
helloworld
root@ubuntu:/demo/helloworld-git#
git branch
ssh-keygen -T rsa -C "1559727195@qq.com"
root@ubuntu:/bin# cd ../
root@ubuntu:/# PATH=~/bin:$PATH
root@ubuntu:/# curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo >~/bin/repo
The program 'curl' is currently not installed. You can install it by typing:
apt-get install curl
root@ubuntu:/#
curl https://gerrit-google.tuna.tsinghua.edu.cn/git-repo/repo >~/bin/repo
https://blog.youkuaiyun.com/qq_28449863/article/details/79978937----Ubuntu16.04下载及编译Android源码
https://lug.ustc.edu.cn/wiki/mirrors/help/aosp ---- AOSP(Android) 镜像使用帮助
root@ubuntu:/usr/share/backgrounds# sudo mv warty-final-ubuntukylin.jpg warty-final-ubuntukylin222.jpg
mv: cannot stat `warty-final-ubuntukylin.jpg': No such file or directory
root@ubuntu:/usr/share/backgrounds#
sudo mv warty-final-ubuntu.png warty-final-ubuntu222.png
sudo mv ubuntu.jpg warty-final-ubuntu.png