Android9 HAL Service 开发(1)

本文将介绍如何在Android P上简单增加绑定式的HAL Service,然后编写一个App直接调用HAL的接口,并使用 aosp_car_x86_64-userdebug 模拟器进行验证。

一 编写.hal文件

.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();
};

 

二 使用 hidl-gen 生成*.h、*.cpp、Android.bp等模板文件

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。

 

三 实现hal 服务端

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

 

四 Device和VNDK部分

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]  #此处为添加

 

五 SeLinux部分

此处直接修改系统的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)

 

六 App客户端实现

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进行验证

Android9 HAL Service 开发(1)_第1张图片

你可能感兴趣的:(Android,Framework,android,ndk)