Android R(11)跨进程调用自定义HIDL服务接口(八)

本文介绍如何使用C++实现HIDL服务的客户端,包括Android.bp配置、客户端代码编写及调试方法。通过具体示例展示了如何调用HIDL提供的接口。

  幸苦添加的HIDL service是为了服务各个client请求的。在实际场景中,HIDL service一般对应某个硬件模块,其中的接口则对应硬件所具备的功能。各个client则是对该模块有具体需求的。例如 HIDL-Light中则会提供控制具体led的接口,那么client则可以通过HIDL-Light所提供的接口来控制led,来实现各种联动功能。

1.Android.bp 说明

  HIDL的服务是支持cpp/ndk/java方式调用的,这里选用cpp的方式进行调用。那么编译的模块则是cc_binary,大致内容如下

cc_binary {
   
   
    name: "customHardwareClient1",
    vendor: true,
    relative_install_path: "hw",
    shared_libs: [
        ...
        "flagstaff.hardware.custom_hardware@1.0",
    ],
}

  从上面的Android.bp可得,测试模块为customHardwareClient1,位于/vendor/bin/hw路径。因为其需要调用custom_hardware接口的服务,则添加相应的动态库(flagstaff.hardware.custom_hardware@1.0),该动态库中涵盖了binder rpc的实现,以及ICustomHardware,.ha接口所转化出来的cpp头文件和代理类相关的接口实现。

2.customHardwareClient1实现

using flagstaff::hardware::custom_hardware::V1_0::ICustomHardware;
int main(int /* argc */, char** /* argv */) {
   
   

	android::sp<ICustomHardware> instance 
<think>我们即将讨论如何通过反射调用HIDL服务HIDL(HALInterface DefinitionLanguage)是Android为硬件抽象层定义的接口语言,用于替换旧的HAL。在Android8.0(Oreo)及以上版本中,系统服务与硬件之间的通信使用HIDL。反射调用HIDL服务的步骤:1.获取HIDL服务的Manager(例如,通过ServiceManager获取)。2.获取服务的IBinder对象。3.通过反射获取HIDL的Stub类(通常以`IHwBinder`形式存在)。4.将IBinder转换为具体的HIDL接口。5.通过反射调用接口的方法。注意:HIDL服务通常运行在系统进程(如system_server)或硬件进程(如vendor进程)中,调用可能需要系统权限。以下是一个示例,展示如何通过反射调用`android.hardware.vibrator@1.0::IVibrator`服务的`on`方法。注意:实际应用中,由于HIDL服务通常需要系统权限,普通应用可能无法调用。此示例主要用于系统级开发或测试。步骤:1.获取ServiceManager。2.通过ServiceManager获取`vibrator`服务的IBinder。3.使用反射获取`android.hardware.vibrator@1.0::IVibrator`的Stub类。4.调用Stub的`asInterface`方法获取接口实例。5.反射调用`on`方法。代码示例: ```javaimport android.os.IBinder; importandroid.os.RemoteException; importandroid.util.Log;import java.lang.reflect.InvocationTargetException; importjava.lang.reflect.Method;public classHidlReflectionHelper {privatestatic finalString TAG= "HidlReflectionHelper";//HIDL服务的全名,格式为"package@version::interface"private staticfinal StringVIBRATOR_HIDL_INTERFACE= "android.hardware.vibrator@1.0::IVibrator";privatestatic finalString VIBRATOR_SERVICE_NAME= "vibrator";publicstatic voidvibrateOn(inttimeoutMs) {try{//1.获取ServiceManager类Class<?> serviceManagerClass= Class.forName("android.os.ServiceManager");//2.获取getService方法Method getServiceMethod= serviceManagerClass.getMethod("getService",String.class);//3.调用getService方法获取vibrator服务的IBinderIBinder binder= (IBinder) getServiceMethod.invoke(null,VIBRATOR_SERVICE_NAME);if(binder ==null){Log.e(TAG, "Failed toget vibrator servicebinder");return;}//4.获取HIDL Stub类Class<?>stubClass= Class.forName(VIBRATOR_HIDL_INTERFACE.replace("::", "$") +"$Stub");//5.获取asInterface方法Method asInterfaceMethod= stubClass.getMethod("asInterface", android.os.IHwBinder.class);//6.调用asInterface方法获取接口实例ObjectvibratorService =asInterfaceMethod.invoke(null, (android.os.IHwBinder) binder);//7.获取on方法Method onMethod =vibratorService.getClass().getMethod("on",int.class);//8.调用on方法onMethod.invoke(vibratorService, timeoutMs);Log.d(TAG, "Vibratoron for" +timeoutMs+ "ms");}catch (ClassNotFoundException| NoSuchMethodException |IllegalAccessException |InvocationTargetException| RemoteException e) {Log.e(TAG,"Errorinvoking vibrator HIDLservice",e);}}} ```注意:-由于HIDL接口在Java中是以内部类的形式存在,所以类名中的`::`需要替换为`$`,并加上`$Stub`。-需要将IBinder转换为`android.os.IHwBinder`(因为HIDL服务使用IHwBinder)。 -调用`on`方法时,参数类型为`int`。但是,上述代码在非系统应用上可能无法运行,因为普通应用无法访问`android.os.ServiceManager`和HIDL接口类。因此,通常需要在系统应用或具有系统权限的环境下运行。另外,从Android10开始,HIDL逐渐被AIDL forHAL取代,但仍有大量设备使用HIDL。高级技巧:如果无法直接访问HIDL类,可以考虑使用`android.os.IHwBinder`和`android.os.HwRemoteBinder`来发送原始请求。但这种方式更复杂,需要构造正确的数据格式。下面是一个使用`IHwBinder`发送原始请求的示例(仅作为高级参考,通常不推荐): ```java//注意:此方法非常底层且复杂,需要了解HIDL的编解码协议publicstatic voidrawVibrateOn(IHwBinderbinder,int timeoutMs)throws RemoteException {//构造HIDL事务int transactionId=1;//每个方法在接口中有固定的事务ID,需要从.hal文件中获取android.os.HwParcelrequest=new android.os.HwParcel();android.os.HwParcel reply= newandroid.os.HwParcel();try {request.writeInterfaceToken(VIBRATOR_HIDL_INTERFACE);request.writeInt32(timeoutMs);binder.transact(transactionId, request, reply,0 /*flags */);reply.verifySuccess();} finally{request.release();reply.release();} }```在实际使用中,需要知道每个方法对应的事务ID(transactionID),这个ID定义在HIDL接口文件中。例如,在`IVibrator.hal`中,`on`方法可能被赋予事务ID1(具体ID需要查看源码或生成的Java文件)。由于这种方式非常脆弱(事务ID可能变化,且需要处理复杂的编解码),因此除非必要,否则不推荐使用。总结:反射调用HIDL服务需要系统权限和正确的类路径,通常用于系统应用或系统开发中。§§相关问题§§: 1. HIDLAIDL有什么区别?在Android中各自的应用场景是什么?2.如何为自定义硬件创建HIDL服务?3.在Android10及以上版本中,AIDLfor HAL如何取代HIDL?4.如何通过反射调用Android系统服务(非HIDL)?</think># Android 反射调用 HIDL 服务的完整指南 HIDL(HAL Interface Definition Language)是 Android 8.0 引入的硬件抽象层接口定义语言,用于替代传统的 HAL。在某些场景下,开发者可能需要通过反射机制访问 HIDL 服务,尤其是在系统级开发或逆向工程中。 ## HIDL 反射调用核心原理 HIDL 服务调用的反射流程: 1. 获取 HIDL 服务的 `IBinder` 对象 2. 通过反射获取 HIDL 接口的 `Stub` 类 3. 使用 `asInterface()` 方法获取代理对象 4. 反射调用目标方法 ## 完整反射实现代码 ```java import android.os.IBinder; import android.os.IHwBinder; import android.os.RemoteException; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class HidlReflectionHelper { private static final String TAG = "HidlReflectionHelper"; /** * 反射调用 HIDL 服务 * * @param serviceName HIDL 服务名称 (如 "android.hardware.vibrator@1.0::IVibrator") * @param methodName 要调用的方法名 * @param params 方法参数 * @return 方法调用结果 */ public static Object invokeHidlService(String serviceName, String methodName, Object... params) { try { // 1. 获取 HwServiceManager Class<?> hwServiceManagerClass = Class.forName("android.os.HwServiceManager"); Method getServiceMethod = hwServiceManagerClass.getMethod("getService", String.class, String.class); // 2. 解析服务名 String[] parts = serviceName.split("::"); if (parts.length != 2) { throw new IllegalArgumentException("Invalid service name format"); } String ifaceName = parts[0]; String instanceName = parts[1]; // 3. 获取 IHwBinder IHwBinder binder = (IHwBinder) getServiceMethod.invoke(null, ifaceName, instanceName); if (binder == null) { throw new RuntimeException("HIDL service not found: " + serviceName); } // 4. 加载 Stub 类 String stubClassName = ifaceName + "$Stub"; Class<?> stubClass = Class.forName(stubClassName); // 5. 获取 asInterface 方法 Method asInterfaceMethod = stubClass.getMethod("asInterface", IHwBinder.class); // 6. 创建服务代理对象 Object serviceProxy = asInterfaceMethod.invoke(null, binder); // 7. 准备参数类型数组 Class<?>[] paramTypes = new Class<?>[params.length]; for (int i = 0; i < params.length; i++) { paramTypes[i] = params[i].getClass(); } // 8. 获取目标方法 Method targetMethod = findMethod(serviceProxy.getClass(), methodName, paramTypes); // 9. 调用服务方法 return targetMethod.invoke(serviceProxy, params); } catch (Exception e) { Log.e(TAG, "Error invoking HIDL service", e); throw new RuntimeException("Failed to invoke HIDL service", e); } } // 查找方法(处理参数类型转换) private static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) throws NoSuchMethodException { for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(methodName)) { Class<?>[] methodParamTypes = method.getParameterTypes(); if (methodParamTypes.length == paramTypes.length) { boolean match = true; for (int i = 0; i < paramTypes.length; i++) { if (!methodParamTypes[i].isAssignableFrom(paramTypes[i])) { match = false; break; } } if (match) { return method; } } } } throw new NoSuchMethodException(methodName + " with matching parameters"); } } ``` ## 使用示例:控制设备震动 ```java public class VibratorController { private static final String VIBRATOR_SERVICE = "android.hardware.vibrator@1.0::IVibrator"; public static void vibrate(int durationMs) { try { HidlReflectionHelper.invokeHidlService( VIBRATOR_SERVICE, "on", durationMs ); Log.d("Vibrator", "Vibration started for " + durationMs + "ms"); } catch (Exception e) { Log.e("Vibrator", "Failed to vibrate", e); } } public static void cancelVibration() { try { HidlReflectionHelper.invokeHidlService( VIBRATOR_SERVICE, "off" ); Log.d("Vibrator", "Vibration cancelled"); } catch (Exception e) { Log.e("Vibrator", "Failed to cancel vibration", e); } } } ``` ## 关键点解析 ### 1. HIDL 服务命名规范 HIDL 服务名称遵循特定格式: ``` <package>@<version>::<interface> ``` 例如: - `android.hardware.vibrator@1.0::IVibrator` - `android.hardware.light@2.0::ILight` ### 2. HIDLAIDL 反射的区别 | 特性 | HIDL 反射 | AIDL 反射 | |------------------|-------------------------------|-------------------------------| | 服务管理器 | `HwServiceManager` | `ServiceManager` | | Binder 类型 | `IHwBinder` | `IBinder` | | 接口转换方法 | `Stub.asInterface(IHwBinder)` | `Stub.asInterface(IBinder)` | | 参数处理 | 使用 `HwBlob`/`HwParcel` | 使用 `Parcel` | | 主要应用场景 | 硬件抽象层服务 | 系统服务和应用服务 | ### 3. 处理复杂数据类型 当传递复杂类型时,需要使用 HIDL 的序列化机制: ```java // 创建自定义类型 public class CustomData { public int value1; public String value2; } // 序列化为 HwParcel public static android.os.HwParcel toHwParcel(CustomData data) { android.os.HwParcel parcel = new android.os.HwParcel(); parcel.writeInt32(data.value1); parcel.writeString(data.value2); return parcel; } // 在反射调用中使用 CustomData data = new CustomData(); data.value1 = 42; data.value2 = "test"; HidlReflectionHelper.invokeHidlService( "custom.package@1.0::IService", "processData", toHwParcel(data) ); ``` ### 4. 权限要求 HIDL 服务通常需要系统权限: ```xml <uses-permission android:name="android.permission.ACCESS_HARDWARE" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> ``` 在 AndroidManifest.xml 中添加: ```xml <application android:sharedUserId="android.uid.system" ...> ``` ## 高级技巧:直接使用 HwBinder 对于更底层的访问,可以直接操作 `IHwBinder`: ```java public static void rawHidlCall(String serviceName, int transactionId, Object... params) { try { // 获取 IHwBinder IHwBinder binder = getHidlBinder(serviceName); // 创建请求和响应 Parcel android.os.HwParcel request = new android.os.HwParcel(); android.os.HwParcel reply = new android.os.HwParcel(); // 写入接口令牌 request.writeInterfaceToken(getInterfaceToken(serviceName)); // 写入参数 for (Object param : params) { if (param instanceof Integer) { request.writeInt32((Integer) param); } else if (param instanceof String) { request.writeString((String) param); } // 处理其他类型... } // 执行事务 binder.transact(transactionId, request, reply, 0); // 读取响应 reply.verifySuccess(); int result = reply.readInt32(); Log.d("RawHidl", "Transaction result: " + result); } catch (Exception e) { Log.e("RawHidl", "Raw HIDL call failed", e); } } private static String getInterfaceToken(String serviceName) { return serviceName.split("::")[0]; // 返回包名@版本 } ``` ## 安全注意事项 1. **权限验证**:HIDL 服务应实现严格的权限检查 ```cpp // 在 HIDL 服务实现中 (C++) Return<void> MyService::on(uint32_t duration) { if (!checkCallingPermission("android.permission.DEVICE_POWER")) { return Void(); } // 实际实现 } ``` 2. **输入验证**:所有参数都应进行有效性检查 3. **异常处理**:捕获所有可能的异常 4. **服务稳定性**:避免频繁调用可能影响硬件稳定性的方法 ## HIDL 2.0 与 AIDL HAL 的演进 从 Android 10 开始,Google 引入了 AIDL HAL 作为 HIDL 的替代方案: - 使用标准的 AIDL 语法 - 更好的版本兼容性 - 更简单的集成方式 对于新项目,建议优先使用 AIDL HAL: ```java // AIDL HAL 服务绑定 IBinder binder = ServiceManager.getService("my.hal.service"); IMyHalService service = IMyHalService.Stub.asInterface(binder); service.doSomething(); ``` ## 常见问题解决 **问题:`ClassNotFoundException` for HIDL Stub** - 确保 HIDL 接口在设备上已实现 - 检查类名是否正确(区分大小写) - 确认设备 Android 版本支持该 HIDL 版本 **问题:`NoSuchMethodException`** - 使用 `getDeclaredMethods()` 检查可用方法 - 确认方法签名(参数类型和顺序) - 检查 HIDL 版本是否匹配 **问题:权限拒绝** - 确保应用具有系统签名 - 在 `AndroidManifest.xml` 中声明所需权限 - 检查 SELinux 策略是否允许访问
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值