我一直觉得,现代计算机不是一门科学,起码快算不上一门理科科学。上上下下全是人造,左左右右全是生意,用管理学,经济学去学计算机,也许更看得懂很多问题。HAL就是一个典型例子。
传统Linux绕开了微软的霸权,但是又面临GPL要开源的尴尬。谷歌为了迎合硬件资本家封闭垄断发财的愿望,所以搞了HAL这个概念。将原来在内核态的驱动,移了大部分核心内容到用户态,这样就可以封闭,可以垄断赚钱。然后因为HAL这玩意只在安卓上有,配合了安卓的硬件公司反过来又被谷歌绑架,这样一环套一环吃钱。一开始用Java,吸引广大的Java生态码农进场。现在又搞Kotlin想把码农套牢。搞这几出,是真的只是科学吗?当然不是,归根结底大部分还是生意。
我对安卓接触不久,有些理解不一定对,大家用批判的眼光看就好。有什么错误还请指正。
一 安卓硬件接口的演化
传统Linux怎么调用硬件?就是ko。。。几个函数搞定。Android复杂了很多,所以整理一下。
在安卓中,除了上面讲的HAL,还有一个就是Framework,这个就是app的framework,要理解安卓驱动层,这两点绕不开。为了适配framework,安卓把硬件是作为服务service来使用。在Android中,这种结构也进化了很多代。
1 首先是传统HAL(Legacy HAL)
在内核8.0以前使用。用户态的so貌似保存在/system/lib64,这个版本是直接读取硬件so,比较原始暴力。
App->Service->AIDL->Server->JNI->HAL so->kernel driver
2 现代HAL(Conventional HAL)
使用Iibhardware来管理so,同时下层的驱动使用一个单独的分区,so全部集成到vender.img,和system解绑。要找寻so通过libhardware这个中间层。编译时要提供一个Android.bp文件。
App->Service->AIDL->Server->JNI->Iibhardware->so(vender.img)
3 HIDL
在安卓8之后,开始使用HIDL,总的来说就是将HAL封装成服务,完全就是rpc的玩法。
HIDL编译后,会生成桩代码。最后会有两个so,一个so,放在system分区;一个so,放在vendor分区,以此实现framework和驱动服务的分离。客户端使用前者,服务端使用后者。服务端包含so之后,会生成一个可执行程序,就可以作为一个service单独运行。客户端包含了so可以直接调用服务。
分为直通式和绑定式,其实很简单,用不同的API就行了,分别是defaultPassthroughServiceImplementation和registerAsService。有大神说直通式就是虽然使用HAL,但还是在一个进程,不走binder。绑定式是使用HAL,但是数据通过Binder转发,上面说的应该就是绑定式。直通式和绑定式也都是HAL生成的代码中控制的。比如:
extern "C" IXXX* HIDL_FETCH_IXXX(const char* name);
AIDL和HIDL的中间就是安卓的framework。
App->AIDL->Service->HIDL->HAL Service->so(vender.img)
示例1,大概就是下面这种结构
Power.c往下是通过systemcall,这部分代码是封在so里面的。
PowerManagerService.cpp是封装下层的so,然后对上提供HIDL的接口。
PowerManagerService.java对下调用HIDL的接口,对上提供AIDL的接口。
DisplayPowerState.java调用AIDL的接口。
大概是这样。
示例2,下面这张图更细一点:
4 全AIDL。AIDL for HAL
(andorid11开始)。这种就更激进了,相当于整个Framework都干掉了。。
App->AIDL->HAL Service->so(vender.img)
二 看一下AIDL和HIDL。
最新的HAL主要涉及到的主要是两个接口,AIDL和HIDL。
首先是AIDL,Android Interface definition language。看资料说是android中两个app交互的IPC方式。首先IPC有很多种,管道,域套接字,message,共享内存,问题是谷歌为什么要搞出来一套新的呢?
从一个AIDL的例子可以看到:
// IMyRemoteService.aidl
interface IMyRemoteService {
int getValue();
void setValue(int value);
}
一边使用:
private final IMyRemoteService.Stub mBinder = new IMyRemoteService.Stub()
另一边使用:
mRemoteService = IMyRemoteService.Stub.asInterface(iBinder);
在接口中主要定义的还是函数,也就是说比管道这些更上层,更适合Java的调用。就算上层函数调用,其实也很多方式,比如COM,soap,rpc都是干这事的。安卓应该还是用binder进行统一管理了吧,毕竟是一个单独的服务。
HIDL
主要还是提供底层cpp驱动和上层java的通信。hal文件定义如下:
// ILedService.hal
package android.hardware.led;
interface ILedService {
int getLedState();
void setLedState(int state);
};
它的C++语法稍怪:
#include <android/hardware/led/1.0/ILedService.h>
#include <hidl/LegacySupport.h>
using android::hardware::led::V1_0::ILedService;
using android::hardware::Return;
using android::hardware::Void;
class LedService : public ILedService {
public:
LedService() {}
Return<int32_t> getLedState() override {
return mLedState;
}
//略
};
int main() {
return android::hardware::configureRpcThreadpool(1, true) == android::OK &&
android::hardware::registerAsService(new LedService()) == android::OK
? 0
: 1;
}
Java侧的倒是差不多。
在Binder的使用中,Server端继承BnInterface, Client端继承BpInterface。我接触的工程中,一边叫做BP侧,那么应该就是app侧,BN侧那么就是服务端了。
部署的时候要注意一下兼容性矩阵:Android VINF和兼容性矩阵-CSDN博客
最后,写完的service可以通过rc文件,作为一个系统服务。
网上文章很多,但是靠谱的不多。
参考:
针对 Android 进行开发 | Android 开发者 | Android Developers
https://source.android.com/docs/core/architecture?hl=zh-cn
https://source.android.com/docs/core/architecture/aidl/aidl-hals?hl=zh-cn
AIDL for HALs - Code Inside Out
AIDL for HALs实战_hidl会被aidl代替-CSDN博客
AIDL for HALs实战_hidl会被aidl代替-CSDN博客