Android上层与驱动交互完整篇(三)HIDL服务

Android上层与驱动交互完整篇(三)HIDL服务

1、服务器端

网上很多教程是在hardware/interface下创建我们的hidl服务,其实各个厂商在设计hidl服务的时候,会放在vendor目录下。这样的好处就是,在不同平台适配时,可以直接平移我们的这部分代码。今天我们就在vendor下创建我们的hidl服务。

cd vendor; mkdir -p ./max/hardware/interfaces/idemo/1.0

idemo即是我们要创建的hidl 服务的目录。
interfaces目录下还要创建几个文件。
1、current.txt 这里会记录各个.hal文件的hashkey的值。假如你修改了hal文件,但是current.txt没有更新,编译时会报错。
2、Android.bp

hidl_package_root {
name: "vendor.max.hardware",
path: "vendor/max/hardware/interfaces",
}
// This is an autogenerated file, do not edit.
subdirs = [
"idemo/1.0",  //假如interfaces多个hidl服务目录,这里要写多个。
]

3、update-base.sh

#!/bin/bash

# WARNING: vendor-base is placed here only for testing.
# It should typically be created elsewhere.

options="-Lexport-header \
     -r vendor.max.hardware:vendor/max/hardware/interfaces \
     -r android.hidl:system/libhidl/transport\
     -r android.hardware:hardware/interfaces"

hidl-gen $options \
     -o system/tools/hidl/test/vendor/1.0/vendor-base.h \
     vendor.max.hardware.idemo@1.0

4 、update-makefiles.sh

#!/bin/bash

source system/tools/hidl/update-makefiles-helper.sh

do_makefiles_update \
"vendor.max.hardware:vendor/max/hardware/interfaces" \
"android.hidl:system/libhidl/transport" \
"android.hardware:hardware/interfaces"

5、在idemo/1.0 下创建IDemo.hal types.hal IDemoCallback.hal三个文件内容如下
IDemo .hal

package vendor.max.hardware.idemo@1.0;
import IDemoCallback;
interface IDemo{

	hwInvoke(string request) generates (string ret);
	hwRegistCallback(IDemoCallback callback) generates (int32_t ret);
};

types.hal

package vendor.max.hardware.idemo@1.0;

enum Result : uint32_t {
	OK,                  // Success
	FAIL,                // Failure, unknown reason
};

IDemoCallback.hal

package vendor.max.hardware.idemo@1.0;

interface IDemoCallback {
	oneway hwNotify(uint32_t type, string reply);
};

6、输入命令生成cpp 以及Android.bp文件,假如源码已经编译过,而且已输入souce build/env_setup.sh, lunch命令

PACKAGE=vendor.max.hardware.idemo@1.0
LOC=vendor/max/hardware/interfaces/idemo/1.0/default/
hidl-gen -o $LOC -Lc++-impl -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
hidl-gen -o $LOC -Landroidbp-impl -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

7 、生成对应的hash 值,全部copy到current.txt中

hidl-gen -L hash -rvendor.max.hardware:vendor/max/hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

8、这样,1.0/default目录下会生成.cpp文件,但是我不习惯把service放在这里编译,相反我会将server代码与client代码放在一起编译,所有这里default目录对于我来说是没用的。
只看1.0目录下的Android.bp即可。

// This file is autogenerated by hidl-gen -Landroidbp.

hidl_interface {
name: "vendor.max.hardware.idemo@1.0",
root: "vendor.max.hardware",
vendor: true,
srcs: [
    "types.hal",
    "IDemo.hal",
    "IDemoCallback.hal",
],
interfaces: [
    "android.hidl.base@1.0",
],
types: [
    "Result",
],
gen_java: true,
}

此bp文件将三个hal文件编译成vendor.max.hardware.idemo@1.0.so,
目录位于/vendor/lib下,编译命令如下:

mmm vendor/max/interfaces/idemo -j32

至此interfaces目录创建完成。

接下来在源码编译app的地方,创建我们的应用或者我们中间件目录,命名为Demo,命令如下

mkdir -p ./Demo/service 

Demo目录下创建Android.mk文件

# Call sub android mks
LOCAL_PATH:= $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))

service目录下创建五个文件

touch main_service.cpp
touch DemoImpl.cpp
touch DemoImpl.h
touch android.mk
touch demo.rc

DemoImpl.h其实对应的是interface/1.0/default/自动生成的Demo.h文件,我们可以参考进行修改。

#ifndef VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H
#define VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H

#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>
#include <log/log.h>
namespace vendor {
namespace max {
namespace hardware {
namespace idemo {
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;

class DemoImpl : public IDemo {

public:
    // Methods from ::vendor::max::hardware::idemo::V1_0::IDemo follow.
    Return<void> hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb) override;
    Return<int32_t> hwRegistCallback(const sp<::vendor::max::hardware::idemo::V1_0::IDemoCallback>& callback) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.
private:
    mutable android::Mutex mLock;
    std::map<uint32_t, sp<IDemoCallback>> mClients;
};

// FIXME: most likely delete, this is only for passthrough implementations
// extern "C" IHrtvMw* HIDL_FETCH_IHrtvMw(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace idemo
}  // namespace hardware
}  // namespace max
}  // namespace vendor

#endif  // VENDOR_MAX_HARDWARE_IDEMO_V1_0_IDEMO_H

DemoImpl.cpp 其实对应的是interface/1.0/default/自动生成的Demo.cpp文件,这里我们也是自己去仿照完成。

#define LOG_TAG "Max@DemoImpl.cpp"

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <hwbinder/Parcel.h>
#include <atomic>
#include <hardware/hardware.h>
#include <utils/threads.h>

#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include "HrtvMwImpl.h"

namespace vendor {
namespace max {
namespace hardware {
namespace idemo {
namespace V1_0 {
namespace implementation {

// Methods from ::vendor::max::hardware::idemo::V1_0::IDemo follow.
Return<void> DemoImpl::hwInvoke(const hidl_string& request, hwInvoke_cb _hidl_cb) {
    // TODO implement
    _hidl_cb(request);    //这里将传入的参数直接返回
    return Void();
}

Return<int32_t> DemoImpl::hwRegistCallback(const sp<::vendor::max::hardware::idemo::V1_0::IDemoCallback>& callback) {
    // TODO implement
    android::AutoMutex _l( mLock );
    if (callback != nullptr) {
        int cookie = -1;
        int clientSize = mClients.size();
        for (int i = 0; i < clientSize; i++) {
            if (mClients[i] == nullptr) {
                ALOGI("%s, client index:%d had died, this id give the new client", __FUNCTION__, i);
                cookie = i;
                mClients[i] = callback;
                break;
            }
        }

        if (cookie < 0) {
            cookie = clientSize;
            mClients[clientSize] = callback;
        }
        ALOGI("%s cookie:%d, client size:%d", __FUNCTION__, cookie, (int)mClients.size());
    }
    return int32_t {};
}


// Methods from ::android::hidl::base::V1_0::IBase follow.

}  // namespace implementation
}  // namespace V1_0
}  // namespace idemo
}  // namespace hardware
}  // namespace max
}  // namespace vendor

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_MODULE:= demo

LOCAL_INIT_RC := demo.rc

LOCAL_SRC_FILES:= \
	DemoImpl.cpp \
	main_service.cpp


LOCAL_CFLAGS += -DHDCP_AUTHENTICATION
LOCAL_CPPFLAGS += -std=c++14
LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)

LOCAL_SHARED_LIBRARIES := \
  	libcutils \
  	libutils \
  	liblog \
  	libbinder \
	libhwbinder \
  	libbase \
	libm \
  	libhidlbase \
  	libhidltransport \
  	vendor.max.hardware.idemo@1.0 

  LOCAL_C_INCLUDES := \
  	external/zlib \
  	external/libcxx/include \
  	system/core/include \
  	system/libhidl/transport/include/hidl \
  	external/googletest/googletest/include \
  	hardware/libhardware/include \

LOCAL_CFLAGS += -D__ANDROID_O
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-sign-compare
LOCAL_CFLAGS += -Wno-pointer-sign
LOCAL_CFLAGS += -Wno-unused-function
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-implicit-function-declaration
LOCAL_CFLAGS += -Wno-unused-result

ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif

include $(BUILD_EXECUTABLE)

demo.rc

on early-boot
start demo

service demo /vendor/bin/demo
    user root
    group system root media audio

main_service.cpp

#define LOG_TAG "Max@Main_Service.cpp"
#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <HidlTransportSupport.h>
#include "DemoImpl.h"


using namespace android;
using ::android::hardware::configureRpcThreadpool;
using ::vendor::max::hardware::idemo::V1_0::implementation::DemoImpl;
using ::vendor::max::hardware::idemo::V1_0::IDemo;

int main()
{
    android::ProcessState::initWithDriver("/dev/vndbinder");
    configureRpcThreadpool(16, false);
    sp<ProcessState> proc(ProcessState::self());

	sp<IDemo> demo = new DemoImpl();
	if (demo == nullptr) {
   		ALOGE("Cannot create IDemo service");
	} else if (demo->registerAsService() != OK) {
    	ALOGE("Cannot register IDemo service.");
	} else {
    	ALOGI("Treble IDemo service created.");
	}
	/*
 	* This thread is just going to process Binder transactions.
 	*/
	IPCThreadState::self()->joinThreadPool();
}

mmm Demo/service -j32
生成vendor/bin/demo以及/vendor/etc/init/demo.rc
至此 service已经搭建完成。

2、客户端

分两种 一种是java,一种是c++。
先讲java代码
这里将在源码下封装一个jar包,提供app调用。
先在上面Demo目录下建立java以及包名目录

mkidir -p java/com/mac/demo
cd java
touch Android.mk
touch com.max.demo.xml
cd java/com/max/demo/
touch DemoManager.java

Android.mk

LOCAL_PATH:= $(call my-dir)
# the library
# ============================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
  $(call all-subdir-java-files)

LOCAL_MODULE := Demo
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DX_FLAGS := --core-library

ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif

LOCAL_JAVA_LIBRARIES := \
	android.hidl.base-V1.0-java \
	android.hidl.manager-V1.0-java

LOCAL_STATIC_JAVA_LIBRARIES := \
    vendor.max.hardware.idemo-V1.0-java 

include $(BUILD_JAVA_LIBRARY)

#copy xml to permissions directory
include $(CLEAR_VARS)
LOCAL_MODULE := com.max.demo.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_SRC_FILES := $(LOCAL_MODULE)

ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/permissions
else
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
endif

include $(BUILD_PREBUILT)

com.max.demo.xml

<?xml version="1.0" encoding="utf-8"?>
<permissions>
<library name="com.max.demo"
    file="/vendor/framework/Demo.jar"/>
</permissions>

DemoManager.java

package com.max.demo;

import android.content.Context;
import android.os.IBinder;
import android.os.HwBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.NoSuchElementException;

import vendor.max.hardware.idemoV1_0.IDemo;

import vendor.max.hardware.idemo.V1_0.IDemoCallback;
import vendor.max.hardware.idemo.V1_0.Result;

public class DemoManager {

	public static final String TAG = "DemoManager";
	private static DemoManager mInstance;

	private IDemo mProxy = null;
	// Mutex for all mutable shared state.
	private final Object mLock = new Object();
	
	public DemoManager(){
        LogUtil.d(TAG,"DemoManager");
        connectToProxy();
	}
	
	private void connectToProxy() {
    	synchronized (mLock) {
        	if (mProxy != null) {
            	return;
       		 }
        	try {
            	mProxy = IDemo.getService();
       		 } catch (RemoteException e) {
            	LogUtil.e(TAG, "connectToProxy: idemo service not responding", e);
       		 }
   		 }
	}

	// 对应interfaces里的hwinvok函数
	public String hwInvoke(string demo) {
		synchronized (mLock) {
			return mProxy.hwInvoke(demo);
		}
	}
	
	// 如果hidl服务里 有两个返回值可以用这种方法获取。
	//public String getBuildTime() {
    //synchronized (mLock) {
        //Mutable<String> resultVal = new Mutable<>();
        //try {
           // mProxy.getBuildTime((int ret, String v) -> {
                            //if (Result.OK == ret) {
                                //resultVal.value = v;
                           // }
                        //});
           // return resultVal.value;
       // } catch (RemoteException e) {
           // Log.e(TAG, "getBuildTime:" + e);
       // }
   // }
    //return "";
//}

	private static class Mutable<E> {
    	public E value;
    	Mutable() {
        	value = null;
    	}

    	Mutable(E value) {
        	this.value = value;
   		 }
	}		 
}

mmm Demo/java -j32
在vendor/framework/生成Demo.jar

app引用jar时候,要将源吗里的copy出来。
应在manifest.xml添加jar的引用。

2、c++

Demo目录下建立cplusplus目录

Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)


LOCAL_MODULE:= democlient

LOCAL_SRC_FILES:= \
	DemoClient.cpp 


LOCAL_CFLAGS += -DHDCP_AUTHENTICATION
LOCAL_CPPFLAGS += -std=c++14
LOCAL_CFLAGS += -DANDROID_PLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)

LOCAL_SHARED_LIBRARIES := \
  	libcutils \
  	libutils \
  	liblog \
  	libbinder \
	libhwbinder \
  	libbase \
	libm \
  	libhidlbase \
  	libhidltransport \
  	vendor.max.hardware.idemo@1.0 

  LOCAL_C_INCLUDES := \
  	external/zlib \
  	external/libcxx/include \
  	system/core/include \
  	system/libhidl/transport/include/hidl \
  	external/googletest/googletest/include \
  	hardware/libhardware/include \

LOCAL_CFLAGS += -D__ANDROID_O
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-sign-compare
LOCAL_CFLAGS += -Wno-pointer-sign
LOCAL_CFLAGS += -Wno-unused-function
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wno-unused-variable
LOCAL_CFLAGS += -Wno-implicit-function-declaration
LOCAL_CFLAGS += -Wno-unused-result

ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26 && echo OK),OK)
LOCAL_PROPRIETARY_MODULE := true
endif

include $(BUILD_EXECUTABLE)

DemoClient.cpp

#define LOG_TAG     "DemoClient.cpp"
#include <vendor/max/hardware/idemo/1.0/IDemo.h>
#include <hidl/LegacySupport.h> 
#include <log/log.h>
#include <stdio.h>

using android::sp;
using vnedor::max::hardware::idemo::V1_0::IDemo;

using ::android::hardware::Return;
using ::android::hardware::hidl_string;
using ::android::hardware::Void;
int main(){
    android::sp<IDemo> hw_device = IDemo::getService();
    if (hw_device == nullptr) {
        ALOGD("...failed to get hello-hidl");
        return -1;
    }
    ALOGD("Max ...success to get hello-hidl....");
    hidl_string demo = "hello world";
	hw_device->hwInvoke(demo,[&](hidl_string result){
		 ALOGD("Max ...success hwInvoke....%s",result.c_str());
	});
}

编译后/vendor/bin/下生成democlient。

以上所有代码都是写文章时写的,并没有真正在源码里编译,如有编译错误,可自行修改。

以上就可以做到app调用hidl服务了,但是如果有很多个函数,是不是hal文件里要定义很多个函数,每次的修改都很复杂,两边都要同步修改。
那么,下篇文章讲解如何用hwInvoke一个函数管理无数个函数的调用。
例如
client传一个函数的id
servce端接收到id后去执行id对应的函数。

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值