本文将介绍如何在Android P上简单增加绑定式的HAL Service,然后编写一个App直接调用HAL的接口,并使用 aosp_car_x86_64-userdebug 模拟器进行验证。
.hal的语言格式是C++和Java的结合体。
以 hardware/interfaces 为根目录,在这个目录下创建 test/1.0 作为我们编写的服务的工作目录。
1.1 新建types.hal(定义结构体,复杂变量可在此定义)
//types.hal
package [email protected];
struct TestEvent{
int32_t what;
string msg;
};
1.2 新建ITestCallback.hal (用于回调使用)
//ITestCallback.hal
package [email protected];
interface ITestCallback{
//oneway 异步操作且无返回值
oneway onTestEvent(TestEvent event);
};
1.3 新建ITest.hal (主接口)
//ITest.hal
package [email protected];
//需要导入其他的接口,types.hal中的结构体不需要手动导入
import [email protected]::ITestCallback;
interface ITest{
init();
//generates 代表返回的数据类型
helloworld(string name) generates (string result);
setCallback(ITestCallback callback) generates (bool res);
release();
};
2.1 hidl-gen 是AOSP中编译生成的工具,因此在之前全编一次,若已经全编,那么只用source和lunch加载一下环境变量。
source build/envsetup.sh
lunch aosp_car_x86_64_userdebug
make -j12
2.2 切换至项目目录 hardware/interfaces/test/1.0 ,并创建hal服务端代码存放的目录 default
2.3 使用 hidl-gen 生成服务端的.cpp和.h文件。 具体hidl-gen 使用方法可参见 hidl-gen --help
hidl-gen -o . -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport default
2.4 使用hidl-gen 生成Android.bp文件。
hidl-gen -o . -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport default
2.5 使用 update-makefile.sh 生成1.0目录下的Android.bp 文件。
//该sh文件在 /hardware/interfaces目录下
sh ../update-makefiles.sh
2.6 因为服务端不需要ITestCallback的具体实现,故删除2.3步生成的TestCallback.cpp和TestCallback.h。
3.1 编辑Test.h文件,为了方便打印Log,我们需要在这里include Log 头文件以及定义TAG。
#ifndef ANDROID_HARDWARE_TEST_V1_0_TEST_H
#define ANDROID_HARDWARE_TEST_V1_0_TEST_H
#include
#include
#include
//增加的部分开始
#include
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "haltest"
//增加的部分结束
namespace android {
namespace hardware {
namespace test {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
struct Test : public ITest {
// Methods from ::android::hardware::test::V1_0::ITest follow.
Return init() override;
Return helloworld(const hidl_string& name, helloworld_cb _hidl_cb) override;
Return setCallback(const sp<::android::hardware::test::V1_0::ITestCallback>& callback) override;
Return release() override;
// Methods from ::android::hidl::base::V1_0::IBase follow.
};
// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" ITest* HIDL_FETCH_ITest(const char* name);
} // namespace implementation
} // namespace V1_0
} // namespace test
} // namespace hardware
} // namespace android
#endif // ANDROID_HARDWARE_TEST_V1_0_TEST_H
3.2 编辑Test.cpp ,主要是给每一个函数增加内容。
#include "Test.h"
namespace android {
namespace hardware {
namespace test {
namespace V1_0 {
namespace implementation {
sp mCallback = nullptr;
// Methods from ::android::hardware::test::V1_0::ITest follow.
Return Test::init() {
ALOGD("init: ");
return Void();
}
Return Test::helloworld(const hidl_string& name, helloworld_cb _hidl_cb) {
ALOGD("helloworld: ");
char buf[100];
::memset(buf, 0x00, 100);
::snprintf(buf, 100, "hello world, %s", name.c_str());
hidl_string result(buf);
_hidl_cb(result);
return Void();
}
Return Test::setCallback(const sp<::android::hardware::test::V1_0::ITestCallback>& callback) {
ALOGD("setCallback: ");
mCallback = callback;
bool res = false;
if(mCallback != nullptr){
ALOGD("setCallback: done");
res = true;
}
return res;
}
Return Test::release() {
ALOGD("release: ");
if(mCallback != nullptr){
TestEvent event;
event.what = 11;
event.msg = "release";
mCallback->onTestEvent(event);
}
return Void();
}
// Methods from ::android::hidl::base::V1_0::IBase follow.
//ITest* HIDL_FETCH_ITest(const char* /* name */) {
//return new Test();
//}
//
} // namespace implementation
} // namespace V1_0
} // namespace test
} // namespace hardware
} // namespace android
3.3 在default目录新建 service.cpp ,作为服务的入口。
#include
#include
#include
#include "Test.h"
#define UNUSED(x) (void)(x) //宏定义,主要为了防止未使用的变量出现编译报错
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using android::hardware::test::V1_0::implementation::Test;
int main(){
configureRpcThreadpool(4, true);
Test test;
auto status = test.registerAsService(); //注册服务
UNUSED(status);
joinRpcThreadpool();
return 0;
}
3.4 添加 service 启动脚本
service test_hal_service /vendor/bin/hw/[email protected]
class hal
user system
group syste
3.5 修改default目录下的Android.bp文件,编译目标修改为可执行文件。因使用Android Log,增加liblog库。增加init_rc配置。
cc_binary {
name: "[email protected]",
relative_install_path: "hw",
defaults: ["hidl_defaults"],
proprietary: true,
init_rc: ["[email protected]"],
srcs: [
"Test.cpp",
"service.cpp",
],
shared_libs: [
"liblog",
"libhidlbase",
"libhidltransport",
"libutils",
"[email protected]",
],
}
3.6 目前目录结构如下,并且可以尝试单编(mmm hardware/interface/test/1.0)
└── 1.0
├── Android.bp
├── default
│ ├── Android.bp
│ ├── [email protected]
│ ├── service.cpp
│ ├── Test.cpp
│ └── Test.h
├── ITestCallback.hal
├── ITest.hal
└── types.hal
4.1 由于此次使用模拟器进行验证,并且lunch 的是 aosp_car_x86_64-userdebug ,所以在device目录下找到 device\generic\car\common\manifest.xml ,其他编译目标选择其他的manifest.xml 。
android.hardware.test
hwbinder
1.0
ITest
default
4.2 在同目录编辑 car.mk ,添加服务执行文件,使其编译进镜像。
PRODUCT_PACKAGES += \
……
[email protected] #此处为添加
此处直接修改系统的sepolicy,即system\sepolicy目录。
我们只修改vendor目录下的sepolicy,即system\sepolicy\vendor\目录下。
5.1 编辑 file_contexts 文件,新增执行文件的SeLinux域。
/(vendor|system/vendor)/bin/hw/android\.hardware\.test@1\.0-service u:object_r:hal_test_default_exec:s0
5.2 新建 hal_test_default.te
type hal_test_default, domain;
hal_server_domain(hal_test_default, hal_test)
type hal_test_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_test_default)
6.1 使用AndroidStudio进行编辑
6.2 ManActivity.java
package com.example.testapp;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.util.Log;
import android.hardware.test.V1_0.ITest;
public class MainActivity extends Activity implements View.OnClickListener {
ITest testService;
private Button init, hello, set, relese;
private Toast mToast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
testService = ITest.getService(); //获取hal Service
}catch (RemoteException e){
e.printStackTrace();
}
init = findViewById(R.id.bt_init);
hello = findViewById(R.id.bt_hello);
set = findViewById(R.id.bt_callback);
relese = findViewById(R.id.bt_release);
init.setOnClickListener(this);
hello.setOnClickListener(this);
set.setOnClickListener(this);
relese.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_init:
try{
testService.init();
}catch (RemoteException e){
e.printStackTrace();
}
break;
case R.id.bt_hello:
try{
String res = testService.helloworld("张三");
Log.d("TestApp", res);
}catch (RemoteException e){
e.printStackTrace();
}
break;
case R.id.bt_callback:
try{
boolean isSet = testService.setCallback(new TestCallback());
Toast.makeText(this, isSet ? "设置成功" : "设置失败", Toast.LENGTH_LONG).show();
}catch (RemoteException e){
e.printStackTrace();
}
break;
case R.id.bt_release:
try{
testService.release();
}catch (RemoteException e){
e.printStackTrace();
}
break;
default :
break;
}
}
}
6.3 TestCallBack.java
package com.example.testapp;
import android.hardware.test.V1_0.ITestCallback;
import android.hardware.test.V1_0.TestEvent;
import android.os.RemoteException;
import android.util.Log;
public class TestCallback extends ITestCallback.Stub {
@Override
public void onTestEvent(TestEvent event) throws RemoteException {
Log.d("TestApp", "回调");
}
}
6.4 Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := TestApp
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
LOCAL_DEX_PREOPT := false
LOCAL_STATIC_JAVA_LIBRARIES := android.hardware.test-V1.0-java
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_PACKAGE)
6.5 将java、src目录以及AndroidManifest.xml、Android.mk放入到 packages/apps/下面的自定义模块目录。
6.6 因需要把此APK编译进入镜像。故在device\generic\car\common\car.mk 添加模块名。
PRODUCT_PACKAGES += \
……
TestApp #此处为添加
7.1 重新整编
7.2 因为hal service的Selinux没有具体配置,有两处违反地方,因此需要关闭Selinux。
//进入shell后,启动root权限,执行
setenforce 0
7.3 在上一步执行完后,我们可以看到该服务已经起来。
7.4 点击App进行验证