目錄
一 ,準備
二,添加hal檔案
三,實作hal檔案
四,添加service
五,啟動rc檔案
六,生成Android.bp檔案
七,SELinux權限
八,編譯
android裝置添加一個外圍裝置,不像嵌入式那麼簡單,系統越複雜加入的方式越雜,但是萬物都有規矩可尋,有方法可走。這篇文章就說說HAL和使用者之間的接口HIDL。
Google:https://source.android.google.cn/devices/architecture/hidl
google 官方這樣解釋 :HAL 接口定義語言(簡稱 HIDL,發音為“hide-l”)是用于指定 HAL 和其使用者之間的接口的一種接口描述語言 (IDL)。
HIDL 允許指定類型和方法調用(會彙集到接口和軟體包中)。從更廣泛的意義上來說,HIDL 是用于在可以獨立編譯的代碼庫之間進行通信的系統。
HIDL 旨在用于程序間通信 (IPC)。程序之間的通信采用 Binder 機制。對于必須與程序相關聯的代碼庫,還可以使用直通模式(在 Java 中不受支援)。
HIDL 可指定資料結構和方法簽名,這些内容會整理歸類到接口(與類相似)中,而接口會彙集到軟體包中。盡管 HIDL 具有一系列不同的關鍵字,但 C++ 和 Java 程式員對 HIDL 的文法并不陌生。此外,HIDL 還使用 Java 樣式的注釋。
在Android8.0開始,google引入了Treble機制,為了友善Android系統的快速移植、更新、穩定,Android引入了HAL Binder機制,把framework和HAL進行隔離,減少了之間的耦合性,使得framework可以被覆寫更新,不用對HAL編譯更新。
用圖檔展示一下兩者的差别:

Android8.0之前,HAL的調用方式為Legacy HAL, HAL都是編譯成so,然後動态連結到各個frameworks service中去,到了8.0,google為了給廠商一些修改時間,保持了Android的向前相容性,另外設計了一套直通式(Passthrough)的HAL,保留動态庫的HAL調用方式,
binderservice連結動态庫Hal的實作。
8.0之後,google規定廠商新增的HAL都需要切換到綁定式HAL(Binderized)。
在從
libhardware
HAL 轉換為 HIDL HAL 的過程中,HAL 實作成為伺服器,而調用 HAL 的程序則成為用戶端。預設實作可提供直通和 Binder 化 HAL,并可能會随着時間而發生變化:
Hal的發展曆程
Legacy Hal:Android 8.0 之前版本的 HAL 都是編譯成 so,然後動态連結到各個 frameworks service 中去。
Passthrough Hal:該模式是為了相容舊版的 HAL,舊版 HAL 實作仍以動态庫的方式提供,隻是 binder service 連結了動态庫 HAL 實作,即 binder service 通過 hw_get_module 連結了舊版的 hal 實作,而使用者端通過與 binder service IPC 通信,間接實作了與舊版 HAL 的互動。
Binderized HAL:HAL 與 使用者調用在不同的程序中,HAL 被寫成 binder service,而使用者接口如 frameworks 作為 binder client 通過 IPC 機制實作跨程序接口調用。
概念介紹到這裡,了解更清楚的話,可以去google官網閱讀相關内容。
實作一個回調案例
代碼結構
├── bearpi
│ └── 1.0
│ ├── Android.bp
│ ├── current.txt
│ ├── default
│ │ ├── Android.bp
│ │ ├── [email protected]
│ │ ├── BearPi.cpp
│ │ ├── BearPi.h
│ │ └── service.cpp
│ ├── IBearPiClientCallback.hal
│ ├── IBearPi.hal
│ ├── test
│ │ ├── Android.bp
│ │ └── BearPiTest.cpp
│ └── types.hal
一 ,準備
我們需要用的hidl-gen 工具去生成檔案,這個工具可以通過編譯得到
source build/envsetup.sh
lunch xxx
make
先整編一下需要添加的aosp。
二,添加hal檔案
在hardware/interfaces目錄下建立bearpi,我們從1.0版本開始,default目錄為實作hal的存放路徑
mkdir -p bearpi/1.0/default
很多現有的HAL實作會與硬體異步通信,需要以異步的方式通知用戶端發生的事件,比如,指令下發後傳回執行狀态。HIDL接口可以用作異步回調。
在bearpi/1.0路徑下建立兩個hal檔案,
IBearPi.hal
package [email protected]; ----------------------------------------------------------------------------------------------->軟體包名
import IBearPiClientCallback; --------------------------------------------------------------------------------------------------------------->
import
語句是用于通路其他軟體包中的軟體包接口和類型的 HIDL 機制
interface IBearPi{
setNotify(IBearPiClientCallback callback) generates (uint64_t bearpiId); --------------------------------------------------->設定回調
test() generates (uint64_t testresult);
};
IBearPiClientCallback.hal
package [email protected];
interface IBearPiClientCallback {
oneway onResult(uint64_t deviceId, BearPiAcquiredInfo acquiredInfo, uint32_t vendorCode);------------------------>回調函數
};
types.hal --------------------------------------------------------------------------------------------------------------------------------------------->
types.hal
檔案并不定義接口,而是定義軟體包中每個接口可以通路的資料類型
package [email protected];
enum BearPiAcquiredInfo : int32_t {
ACQUIRED_GOOD = 0,
ACQUIRED_PARTIAL = 1,
ACQUIRED_INSUFFICIENT = 2,
ACQUIRED_IMAGER_DIRTY = 3,
ACQUIRED_TOO_SLOW = 4,
ACQUIRED_TOO_FAST = 5,
ACQUIRED_VENDOR = 6
};
注意軟體包字首對應的位置,android.hardware.* -----hardware/interfaces) {---------------------------------------------->打開就是直通模式,注釋使用綁定式
//return new BearPi();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace bearpi
} // namespace hardware
} // namespace android
四,添加service
在default路徑下建立service.cpp
#define LOG_TAG "fht"
#include <android/log.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include <android/hardware/bearpi/1.0/IBearPi.h>
#include <android/hardware/bearpi/1.0/types.h>
#include "BearPi.h"
using android::hardware::bearpi::V1_0::IBearPi;
using android::hardware::bearpi::V1_0::implementation::BearPi;
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::sp;
int main(){
ALOGD("Bearpi service start ");
android::sp<IBearPi> bear = BearPi::getInstance();
//綁定式服務
// This function must be called before you join to ensure the proper
// number of threads are created. The threadpool will never exceed
// size one because of this call.
//如果“callerWillJoin”為true,則表示調用方将加入線程池,是以核心需要少建立一個線程。
//如果“callerWillJoin”為false,我們仍然會在本地生成一個線程,我們還應該告訴核心建立的線程比這裡請求的少一個。
configureRpcThreadpool(1, true ); // 用于設定 目前程序用于hwbinder通信的最大線程數
if (bear != nullptr) {
if (::android::OK != bear->registerAsService()) { //注冊服務
return 1;
}
} else {
ALOGE("Can't create instance of bearpi, nullptr");
}
ALOGD("Bearpi service registerAsService ");
// Adds this thread to the threadpool, resulting in one total
// thread in the threadpool. We could also do other things, but
// would have to specify 'false' to willJoin in configureRpcThreadpool
joinRpcThreadpool();
return 0; // should never get here
//直通式服務,
defaultPassthroughServiceImplementation
将對提供的
-impl
庫執行
dlopen()
操作,并将其作為 Binder 化服務提供
//return defaultPassthroughServiceImplementation<IBearPi>("BearPi");
}
五,啟動rc檔案
在default路徑下建立 [email protected]
service vendor.bear_hal /vendor/bin/hw/[email protected]
# "class hal" causes a race condition on some devices due to files created
# in /data. As a workaround, postpone startup until later in boot once
# /data is mounted.
class hal
user system
group system system
六,生成Android.bp檔案
檔案建立成功後,需要加入編譯,下面我們使用hidl-gen生成對應的Android.bp檔案
生成1.0架構接口庫
hidl-gen -Landroidbp -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]
生成impl 庫的 Android.bp
hidl-gen -Landroidbp-impl -o /home/ubuntu/data/LG/hardware/interfaces/bearpi/1.0/default/ -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport [email protected]
修改default下面Android.bp,生成service bin
cc_binary {
name: "[email protected]",
defaults: ["hidl_defaults"],
init_rc: ["[email protected]"],
vendor: true,
relative_install_path: "hw",
srcs: [
"BearPi.cpp",
"service.cpp",
],
shared_libs: [
"libcutils",
"liblog",
"libhidlbase",
"libhardware",
"libutils",
- "[email protected]",
],
}
七,SELinux權限
以mtk平台為例,添加bearpi相應權限
路徑:device/mediatek/sepolicy/basic/non_plat
1,建立bearpi.te
type bearpi, domain;
type bearpi_exec, exec_type, vendor_file_type, file_type;
hwbinder_use(bearpi);
init_daemon_domain(bearpi)
add_hwservice(bearpi, hal_bearpi_hwservice)
allow bearpi hwservicemanager_prop:file { read open getattr map };
2,file_contexts中添加
/(vendor|system/vendor)/bin/hw/android\.hardware\[email protected]\.0-service u:object_r:bearpi_exec:s0
3,hwservice.te中定義hwservice manager類型
type hal_bearpi_hwservice, hwservice_manager_type;
4,hwservice_contexts中添加接口通路權限
android.hardware.bearpi::IBearPi u:object_r:hal_bearpi_hwservice:s0
八,編譯
1,添加系統編譯,使系統整編後能主動編譯,在device/xx/device.mk中添加包編譯
PRODUCT_PACKAGES += \
[email protected] \
[email protected] \
test_bearpi \
bearpi.default
DEVICE_MANIFEST_FILE += device/xiaomi/lancelot/manifest_bearpi.xml
2,在項目地下建立manifest_bearpi.xml接口注冊檔案,如果沒有注冊檔案,用戶端和服務端将無法通信,getService()将傳回null。
<manifest version="1.0" type="device">
<hal format="hidl">
<name>android.hardware.bearpi</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IBearPi</name>
<instance>default</instance>
</interface>
</hal>
</manifest>
3,添加啟動服務,在init.project.rc内添加bearpi 的rc檔案,保證開機能夠啟動服務
import /vendor/etc/init/[email protected]
----------------------------------------------------------我------------是-------------分-----------割------------線---------------------------------------------------------------
如上服務端寫好後,我們寫用戶端測試下
在1.0路徑下建立test目錄
建立BearPiTest.cpp
//
// Created by ubuntu on 2020/12/25.
//
#define LOG_TAG "fht"
#define LOG_VERBOSE "fht"
#include <android/hardware/bearpi/1.0/IBearPi.h>
#include <hidl/Status.h>
#include <log/log.h>
#include <android/log.h>
using android::hardware::bearpi::V1_0::BearPiAcquiredInfo;
using android::hardware::bearpi::V1_0::IBearPi;
using android::hardware::bearpi::V1_0::IBearPiClientCallback;
using android::sp;
using android::hardware::hidl_string;
using android::hardware::Return; // 命名空間
using android::hardware::Void; // Void函數
//實作回調
class BearPiClientCallback : public IBearPiClientCallback {
public:
BearPiClientCallback() {}
~BearPiClientCallback() {}
Return<void> onResult(uint64_t deviceId, const ::android::hardware::bearpi::V1_0::BearPiAcquiredInfo acquiredInfo,uint32_t vendorCode) override {
ALOGD("onResult deviceid =%lu, vendorCode = %d, info = %d", deviceId, vendorCode, acquiredInfo);
return Void();
};
};
int main() {
//獲得服務
sp <IBearPi> bear = IBearPi::getService();
if (bear == nullptr) {
ALOGE("Can't find IBearPi service ...");
return -1;
}
ALOGD("IBearPi ON");
sp <BearPiClientCallback> callback = new BearPiClientCallback();
//注冊回調
bear->setNotify(callback);
bear->test();
return 0;
}
在test目錄下建立Android.bp
cc_binary {
name: "test_bearpi",
srcs: [
"BearPiTest.cpp",
],
shared_libs: [
"libcutils",
"liblog",
"libhidlbase",
"libhardware",
"libutils",
"[email protected]",
],
}