前言
需求是這樣的,system_server有一個LocalService,需要向一個具有root權限的native進程進行消息傳遞(下發請求指令,獲取狀態信息等)
評估
首先附上官方介紹:https://source.android.com/devices/architecture/hidl
此功能最早使用socket實現(參考lmkd的實現),但是由於如下幾點原因,因此決定使用HIDL重構:
1、socket傳輸效率低,開銷大,通信過程中至少有兩次拷貝過程;
2、由於1的原因,從system_server調用方法主動獲取native進程狀態時效性比binder通信差;
3、由於我們使用的SoC平臺普遍性能較差,因此爲了保證整機性能,需要儘可能減少system_server的壓力;
4、符合Android設計規範,實現更優雅……(汗)
同時考慮到該需求有如下幾點特點:
1、通信兩側語言不同(JAVA - C/C++),雖然可以手動添加一層JNI來實現語言對等,但是能偷懶就偷懶唄;
2、改動需要符合CTS/VTS認證,不能出現neverallow的sepolicy,並且有利於後期推送OS升級的FOTA;
3、native部分儘可能模塊化,甚至可以閉源;
因此使用binderized HIDL看上去是一個比較好的選擇;
實現步驟
1、添加本地.hal文件:
mkdir -p hardware/interfaces/example/1.0/
touch hardware/interfaces/example/1.0/IExample.hal
emacs -nw hardware/interfaces/example/1.0/IExample.hal
由於是測試用,就寫個簡單的Hello World:
package [email protected];
interface IExample {
helloWorld(string in) generates (string out);
};
2、使用hidl-gen命令自動生成相關文件:
首先生成.hal文件對應的Android.bp:
hidl-gen -Landroidbp -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
然後根據.hal文件生成實現類與頭文件:
hidl-gen -o hardware/interfaces/example/1.0/default/ -Landroidbp-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
最後生成實現類的Android.bp:
hidl-gen -o hardware/interfaces/example/1.0/default/ -Lc++-impl -rvendor.zsui.hardware:hardware/interfaces/ -randroid.hidl:system/libhidl/transport [email protected]
若無報錯,最後目錄結構應該是這樣的:
hardware/interfaces/example/
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── Example.cpp
│ └── Example.h
└── IExample.hal
簡單解讀一下:
1.0目錄下的Android.bp用於解釋HIDL接口相關信息;
default目錄下的Android.bp用於描述HIDL接口實際實現的相關信息;
不太嚴謹地用AIDL類比一下,1.0目錄下的.hal文件可以類比爲.aidl文件,而default目錄下的.cpp和.h文件就是這個接口的具體實現;
此例中的兩個目標模板就創建完成了,並且生成了對應的Android.bp,照理話說此時兩個模塊已經可以進行編譯了,但是隻要你試一下,就會發現如下報錯:
$ make [email protected]
error: hardware/interfaces/example/1.0/Android.bp:9:15: module "[email protected]_interface": interfaces: Cannot find package root specification for package root 'vendor.zsui.hardware' needed for module '[email protected]'. Either this is a mispelling of the package root, or a new hidl_package_root module needs to be added. For example, you can fix this error by adding the following to <some path>/Android.bp:
hidl_package_root {
name: "vendor.zsui.hardware",
path: "<some path>",
}
This corresponds to the "-rvendor.zsui.hardware:<some path>" option that would be passed into hidl-gen.
ninja: build stopped: subcommand failed.
19:34:12 soong bootstrap failed with: exit status 1
這個是因爲之前的參數-rvendor.zsui.hardware:hardware/interfaces/指定hardware/interfaces/爲包名vendor.zsui.hardware的package root,但是該路徑下並沒有定義vendor.zsui.hardware包名的package root;
解決方案有三個,鑑於Google架構設計考慮,建議採用c:
a. 修改包名爲hardware/interfaces/Android.bp中定義的hidl_package_root的name,即"android.hardware";
b. 在hardware/interfaces/Android.bp中添加name爲的hidl_package_root;
hidl_package_root {
name: "vendor.zsui.hardware",
path: "vendor/zsui/proprietary/hardware/interfaces/",
}
c. 使用其他目錄結構,並手動聲明hidl_package_root,例如:
mkdir -p vendor/zsui/proprietary/hardware/interfaces/
mv hardware/interfaces/example/ vendor/zsui/proprietary/hardware/interfaces/
touch vendor/zsui/proprietary/hardware/interfaces/Android.bp
emacs -nw vendor/zsui/proprietary/hardware/interfaces/Android.bp
在Android.bp中添加:
hidl_package_root {
name: "vendor.zsui.hardware",
path: "vendor/zsui/proprietary/hardware/interfaces/",
}
然後編譯就可以成功了;
只是裏面沒有實現,也不會有任何服務跑起來,只是兩個.so庫而已;
out/target/product/xxx/system/lib(64)/[email protected]
out/target/product/xxx/vendor/lib(64)/hw/[email protected]
3、實現HIDL
上一步已經說了,default目錄下就是我們接口的具體實現,因此我們只需要把實現代碼完成:
完善Example.cpp:
#include "Example.h"
namespace vendor {
namespace zsui {
namespace hardware {
namespace example {
namespace V1_0 {
namespace implementation {
// Methods from ::vendor::zsui::hardware::example::V1_0::IExample follow.
Return<void> Example::helloWorld(const hidl_string& name, helloWorld_cb _hidl_cb) {
// TODO implement
char buf[100];
memset(buf, 0, 100);
snprintf(buf, 100, "Hello World, %s", name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//IExample* HIDL_FETCH_IExample(const char* /* name */) {
//return new Example();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace example
} // namespace hardware
} // namespace zsui
} // namespace vendor
示例用,就返回了個字符串拼接;
4、binder化HIDL
現在實現和接口都有了,也都可以編譯了,問題就剩下:誰是服務端,誰是客戶端了;
由於使用binderized HIDL,因此我們可以直接基於impl創建服務端:
a. 創建兩個文件
touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/service.cpp
touch vendor/zsui/proprietary/hardware/interfaces/example/1.0/default/[email protected]
其中service.cpp爲服務入口,[email protected]爲啓動相關注冊信息;
b. 在service.cpp中添加入口函數並註冊服務:
#include <vendor/zsui/hardware/example/1.0/IExample.h>
#define LOG_TAG "Example-hal"
#include <hidl/LegacySupport.h>
#include "Example.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using vendor::zsui::hardware::example::V1_0::IExample;
using vendor::zsui::hardware::example::V1_0::implementation::Example;
int main() {
configureRpcThreadpool(2, true /*callerWillJoin*/);
android::sp<IExample> service = new Example();
if(service->registerAsService() != android::OK){
ALOGE("hal registration FAILED");
} else {
ALOGV("hal ready");
joinRpcThreadpool();
}
ALOGW("hal exiting");
return 0;
}
c. 由於我們需要編譯出可執行的二進制文件,而不是一個.so庫,因此需要修改Android.bp:(注意//Modify與//Add字段)
cc_binary { //Modify
// FIXME: this should only be -impl for a passthrough hal.
// In most cases, to convert this to a binderized implementation, you should:
// - change '-impl' to '-service' here and make it a cc_binary instead of a
// cc_library_shared.
// - add a *.rc file for this module.
// - delete HIDL_FETCH_I* functions.
// - call configureRpcThreadpool and registerAsService on the instance.
// You may also want to append '-impl/-service' with a specific identifier like
// '-vendor' or '-<hardware identifier>' etc to distinguish it.
name: "[email protected]", //Modify
relative_install_path: "hw",
// FIXME: this should be 'vendor: true' for modules that will eventually be
// on AOSP.
proprietary: true,
defaults: ["hidl_defaults"], //Add
srcs: [
"Example.cpp",
"service.cpp", //Add
],
shared_libs: [
"libhidlbase",
"libhidltransport",
"libutils",
"liblog", //Add
"[email protected]",
],
init_rc: ["[email protected]"], //Add
}
d. 添加對應服務啓動信息到[email protected]:
service example-hal-1-0 /vendor/bin/hw/[email protected]
class hal
user root
group system
writepid /dev/cpuset/system-background/tasks
至此,編譯下[email protected],應該是可以編譯過的;
服務端的創建差不多就到這裏了,下一篇會着重講解sepolicy的設置、以及JAVA端調用的方式;
文筆有限,若有謬誤,還請指出;
感謝!